{"record":{"id":"fd9650c221c30850","repo":"can1357/oh-my-pi","slug":"schema-contains-unresolved-ref-after-dereferencin","errorCode":null,"errorMessage":"schema contains unresolved $ref after dereferencing","messagePattern":"schema contains unresolved \\$ref after dereferencing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/yield.ts","lineNumber":329,"sourceCode":"\t\t\t\tif (strictProbe.strict) {\n\t\t\t\t\tsanitizedSchema = sanitizeSchemaForStrictMode(normalizedSchema);\n\t\t\t\t} else {\n\t\t\t\t\tsanitizedSchema = normalizedSchema;\n\t\t\t\t\tthis.strict = false;\n\t\t\t\t}\n\t\t\t} else if (!schemaError && normalized === true) {\n\t\t\t\tsanitizedSchema = {};\n\t\t\t\tthis.strict = false;\n\t\t\t}\n\n\t\t\tlet dataSchema: Record<string, unknown>;\n\t\t\tif (sanitizedSchema !== undefined) {\n\t\t\t\tconst resolved = dereferenceJsonSchema({\n\t\t\t\t\t...sanitizedSchema,\n\t\t\t\t\tdescription: schemaDescription,\n\t\t\t\t}) as Record<string, unknown>;\n\t\t\t\tif (hasUnresolvedRefs(resolved)) {\n\t\t\t\t\tthrow new Error(\"schema contains unresolved $ref after dereferencing\");\n\t\t\t\t}\n\t\t\t\tdataSchema = withSectionVariants(resolved);\n\t\t\t} else {\n\t\t\t\tthis.strict = false;\n\t\t\t\tdataSchema = looseRecordSchema(\n\t\t\t\t\tschemaError ? schemaDescription : \"Structured JSON output (no schema specified)\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tparameters = wrapYieldParameters(dataSchema);\n\t\t\tJSON.stringify(parameters);\n\t\t\tif (!isValidJsonSchema(parameters)) throw new Error(\"yield parameters schema is invalid\");\n\t\t} catch (err) {\n\t\t\tconst errorMsg = err instanceof Error ? err.message : String(err);\n\t\t\tparameters = wrapYieldParameters(\n\t\t\t\tlooseRecordSchema(`Structured JSON output (schema processing failed: ${errorMsg})`),\n\t\t\t);\n\t\t\tvalidate = undefined;\n\t\t\tthis.strict = false;","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/yield.ts#L311-L347","documentation":"The YieldTool constructor dereferences the caller-supplied JSON schema (dereferenceJsonSchema) before using it for structured output. If the resolved schema still contains unresolved $ref pointers (hasUnresolvedRefs), the constructor throws — meaning the schema references definitions that were never included, so structured-output providers would produce invalid requests.","triggerScenarios":"Constructing YieldTool with a schema containing `$ref` pointers to definitions absent from the schema document (e.g. `#/$defs/Foo` with no $defs.Foo, or external `$ref` URLs).","commonSituations":"Passing a zod/ajv-serialized schema fragment whose `$defs` were in another document; hand-assembled schemas; reusing a sub-schema extracted from a larger spec.","solutions":["Inline the referenced definitions into the schema's `$defs`/`definitions`.","Replace `$ref` with the fully expanded schema object (inline the target).","Run a dereference/inlining step (e.g. ajv or $RefParser) on the schema before passing it.","Remove the schema to fall back to loose record schema if strict structure isn't required."],"exampleFix":"// before\nyield({ schema: { type: \"object\", properties: { x: { $ref: \"#/$defs/X\" } } } })\n// after\nyield({ schema: { type: \"object\", properties: { x: { $ref: \"#/$defs/X\" } } }, $defs: { X: { type: \"string\" } } })","handlingStrategy":"validation","validationCode":"function hasRefs(schema: object): boolean { return JSON.stringify(schema).includes('\"$ref\"'); } // if true, ensure all $defs are embedded before passing","typeGuard":"function schemaRefsResolve(schema: { $ref?: string; $defs?: Record<string, unknown> }): boolean { return !schema.$ref || (schema.$defs != null && schema.$ref.startsWith(\"#/$defs/\") && schema.$defs[schema.$ref.slice(8)] !== undefined); }","tryCatchPattern":"try { const tool = new YieldTool({ schema }); } catch (e) { if (e.message === \"schema contains unresolved $ref after dereferencing\") { const inlined = inlineRefs(schema); const tool = new YieldTool({ schema: inlined }); } else throw e; }","preventionTips":["Bundle/inlne all $refs (ajv, @apidevtools/json-schema-ref-parser) before constructing YieldTool.","Include $defs in the same document as the schema.","Avoid external/URL $refs entirely for tool schemas.","Validate the schema with a meta-schema validator up front."],"tags":["json-schema","yield","dangling-ref"],"backgroundTag":"unresolved-schema-ref","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}