diff --git a/packages/ai/src/utils/schema/json-schema-validator.ts b/packages/ai/src/utils/schema/json-schema-validator.ts index c6bf82dec..6a55575ea 100644 --- a/packages/ai/src/utils/schema/json-schema-validator.ts +++ b/packages/ai/src/utils/schema/json-schema-validator.ts @@ -153,7 +153,10 @@ function validateSchemaNode( if (ctx.seenPairs.has(pairKey)) return true; ctx.seenPairs.add(pairKey); } else { - if (ctx.refDepth >= MAX_REF_DEPTH) return true; + if (ctx.refDepth >= MAX_REF_DEPTH) { + pushIssue(issues, path, "reference depth exceeded", { keyword: "$ref" }); + return false; + } ctx.refDepth += 1; } const ok = validateSchemaNode(resolved, value, path, ctx, issues); diff --git a/packages/ai/test/schema-strict-mode.test.ts b/packages/ai/test/schema-strict-mode.test.ts index 9bf053b3a..aca4c9485 100644 --- a/packages/ai/test/schema-strict-mode.test.ts +++ b/packages/ai/test/schema-strict-mode.test.ts @@ -583,6 +583,17 @@ describe("json-schema validator unsupported-keyword regressions", () => { expect(isJsonSchemaValueValid(schema, { name: "root", child: { name: 123 } })).toBe(false); expect(isJsonSchemaValueValid(schema, { name: "root", child: { child: { name: "x" } } })).toBe(false); }); + + it("fails primitive $ref chains that exceed the recursion cap instead of accepting invalid values", () => { + const definitions: Record = {}; + for (let i = 0; i < 66; i += 1) { + definitions[`A${i}`] = { $ref: `#/definitions/A${i + 1}` }; + } + definitions.A66 = { type: "number" }; + + const schema = { $ref: "#/definitions/A0", definitions }; + expect(isJsonSchemaValueValid(schema, "not-a-number")).toBe(false); + }); }); describe("meta-validator conditional keywords", () => {