can1357/oh-my-pi · error

schema contains unresolved $ref after dereferencing

Error message

schema contains unresolved $ref after dereferencing

What it means

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.

Source

Thrown at packages/coding-agent/src/tools/yield.ts:329

				if (strictProbe.strict) {
					sanitizedSchema = sanitizeSchemaForStrictMode(normalizedSchema);
				} else {
					sanitizedSchema = normalizedSchema;
					this.strict = false;
				}
			} else if (!schemaError && normalized === true) {
				sanitizedSchema = {};
				this.strict = false;
			}

			let dataSchema: Record<string, unknown>;
			if (sanitizedSchema !== undefined) {
				const resolved = dereferenceJsonSchema({
					...sanitizedSchema,
					description: schemaDescription,
				}) as Record<string, unknown>;
				if (hasUnresolvedRefs(resolved)) {
					throw new Error("schema contains unresolved $ref after dereferencing");
				}
				dataSchema = withSectionVariants(resolved);
			} else {
				this.strict = false;
				dataSchema = looseRecordSchema(
					schemaError ? schemaDescription : "Structured JSON output (no schema specified)",
				);
			}
			parameters = wrapYieldParameters(dataSchema);
			JSON.stringify(parameters);
			if (!isValidJsonSchema(parameters)) throw new Error("yield parameters schema is invalid");
		} catch (err) {
			const errorMsg = err instanceof Error ? err.message : String(err);
			parameters = wrapYieldParameters(
				looseRecordSchema(`Structured JSON output (schema processing failed: ${errorMsg})`),
			);
			validate = undefined;
			this.strict = false;

View on GitHub (pinned to 9690622007)

Solutions

  1. Inline the referenced definitions into the schema's `$defs`/`definitions`.
  2. Replace `$ref` with the fully expanded schema object (inline the target).
  3. Run a dereference/inlining step (e.g. ajv or $RefParser) on the schema before passing it.
  4. Remove the schema to fall back to loose record schema if strict structure isn't required.

Example fix

// before
yield({ schema: { type: "object", properties: { x: { $ref: "#/$defs/X" } } } })
// after
yield({ schema: { type: "object", properties: { x: { $ref: "#/$defs/X" } } }, $defs: { X: { type: "string" } } })
Defensive patterns

Strategy: validation

Validate before calling

function hasRefs(schema: object): boolean { return JSON.stringify(schema).includes('"$ref"'); } // if true, ensure all $defs are embedded before passing

Type guard

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); }

Try / catch

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; }

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/fd9650c221c30850. Report an issue: GitHub.