From 8baf8666a0b1588c44340dbe8b2901d00d9f6db2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 13:20:24 +0000 Subject: [PATCH] improve missing-required-field error messages with richer type descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a required object field is absent, the validation error now reports a meaningful type hint for all schema kinds — array, object, nullable, and enum — instead of falling back to the generic 'value'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- skills/rig/rig.ts | 21 ++++++++++++++++++++- src/rig.test.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/skills/rig/rig.ts b/skills/rig/rig.ts index cb89df7..d659929 100644 --- a/skills/rig/rig.ts +++ b/skills/rig/rig.ts @@ -1455,7 +1455,7 @@ function validateSchema(value: unknown, schema: Schema, path: string, optional: const fieldPath = `${path}.${key}`; const isOptional = isOptionalSchema(fieldSchema); if (fieldValue === undefined && !isOptional) { - const expectedType = "type" in fieldSchema ? (fieldSchema as { type: string }).type : "enum" in fieldSchema ? "enum value" : "value"; + const expectedType = describeSchemaType(fieldSchema); return { ok: false, error: `${fieldPath}: missing required field (expected ${expectedType})` }; } const result = validateSchema(fieldValue, fieldSchema, fieldPath, false); @@ -1789,6 +1789,25 @@ function previewWithFirstDiff(value: string | undefined, expected: string, maxPr return `${prefixEllipsis}${preview}${suffixEllipsis}`; } +function describeSchemaType(schema: Schema): string { + if ("nullable" in schema && schema.nullable === true) { + return `${describeSchemaType((schema as NullableSchema).inner)} | null`; + } + if ("type" in schema) { + return (schema as { type: string }).type; + } + if ("enum" in schema) { + return (schema as EnumSchema).enum.map((v: Json) => JSON.stringify(v)).join(" | "); + } + if ("items" in schema) { + return "array"; + } + if ("properties" in schema || "additionalProperties" in schema) { + return "object"; + } + return "value"; +} + function bad(path: string, expected: string, actual: unknown): ValidationResult { const actualType = actual === null ? "null" : Array.isArray(actual) ? "array" : typeof actual; const actualRepr = JSON.stringify(actual); diff --git a/src/rig.test.ts b/src/rig.test.ts index 6eb51e4..f559da9 100644 --- a/src/rig.test.ts +++ b/src/rig.test.ts @@ -1246,4 +1246,37 @@ describe("missing required field error message", () => { const result = analyzeResponse(JSON.stringify({ name: "Alice" }), schema, "test", 1); expect(result.ok).toBe(true); }); + + it("reports a missing required array field with type 'array'", () => { + const schema = s.object({ items: s.array(s.string), name: s.string }); + const result = analyzeResponse(JSON.stringify({ name: "x" }), schema, "test", 1); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toContain("missing required field"); + expect(result.error.message).toContain("$.items"); + expect(result.error.message).toContain("array"); + } + }); + + it("reports a missing required nullable field with ' | null'", () => { + const schema = s.object({ score: s.nullable(s.number), name: s.string }); + const result = analyzeResponse(JSON.stringify({ name: "x" }), schema, "test", 1); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toContain("missing required field"); + expect(result.error.message).toContain("$.score"); + expect(result.error.message).toContain("number | null"); + } + }); + + it("reports a missing required enum field with enum values", () => { + const schema = s.object({ status: s.enum("open", "closed"), name: s.string }); + const result = analyzeResponse(JSON.stringify({ name: "x" }), schema, "test", 1); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.message).toContain("missing required field"); + expect(result.error.message).toContain("$.status"); + expect(result.error.message).toContain('"open"'); + } + }); });