All articles
Engineering

JSON Schema Validation Errors: Causes, Examples, and Fixes

Fix required, type, additionalProperties, format, oneOf, and $ref errors by reading JSON Pointer paths and isolating the failing schema rule.

Kuda Zafevere·Full-Stack Engineer
·18 min read
Share:

Answer in brief

To fix a JSON Schema validation error, pair the failing instance path with the schema keyword, confirm the active draft, and reduce the schema and JSON data to the smallest reproducible pair before changing either one.

Use this guide as a diagnostic workflow, then verify the result against your production environment and the primary documentation below.

A JSON Schema validator rarely says “change line 18 from a string to an integer.” It reports a path, a keyword such as required or oneOf, and a message whose usefulness depends on the validator. Nested schemas can then produce a wall of secondary errors that makes the original mistake look more complicated than it is.

The reliable way to debug JSON Schema validation errors is to separate three questions: which instance value failed, which schema rule evaluated it, and whether that rule expresses the intended contract. Once those are clear, most failures reduce to a missing property, a runtime type mismatch, an over-closed object, an ambiguous composition branch, or a reference resolved under the wrong base URI or draft.

TL;DR

  • Follow the instance path before interpreting the message.
  • Pair it with the keyword and schema path that failed.
  • Confirm the dialect declared by $schema and the validator configured to evaluate it.
  • Treat required, type, and leaf constraints as likely root causes; composition errors are often summaries.
  • Reduce the instance and schema to the smallest failing pair before editing a complex production contract.

Validate the data and schema side by side.

Paste both into the CodeAva JSON Schema Validator to inspect validation failures, format JSON consistently, and generate a starting schema when you only have a sample payload. Keep the generated schema as a draft—not as proof of your business rules.

Open the JSON Schema Validator & Generator

Start with the anatomy of the error

Validator output is not completely standardized at the library API level. Draft 2020-12 defines JSON Schema output concepts such as an instance location and keyword location, but libraries expose them under different field names. Ajv, for example, commonly reportsinstancePath, schemaPath, keyword, params, and message.

{
  "instancePath": "/customer/age",
  "schemaPath": "#/properties/customer/properties/age/type",
  "keyword": "type",
  "params": { "type": "integer" },
  "message": "must be integer"
}

Read that object from left to right as: the value at /customer/age failed thetype rule located at the reported schema path; the expected type was integer. The message is a summary. The paths and keyword provide the debugging context.

Error fieldWhat it tells youDebugging action
Instance path or locationThe value in the JSON instance being validated.Navigate to this value first. An empty path means the instance root.
Schema pathThe schema keyword or subschema that produced the result.Confirm that this is the schema branch you expected to run.
KeywordThe failed rule, such as type, required, or oneOf.Apply that keyword's semantics instead of guessing from prose.
ParametersStructured details such as a missing or additional property.Use these details to build a precise, user-facing error.

JSON Pointer paths: how to locate the actual failure

Instance locations are commonly represented as JSON Pointers. Each slash moves one level into an object or array. Array indexes are zero-based, so /orders/2/sku points to the sku field of the third item in orders. An empty pointer refers to the document root.

{
  "orders": [
    { "sku": "A-100" },
    { "sku": "B-200" },
    { "sku": 300 }
  ]
}

/orders/2/sku  ->  300

JSON Pointer escaping matters when keys contain special characters. ~1 means a literal slash and ~0 means a literal tilde. Therefore, the key a/b is addressed as /a~1b. Do not treat a pointer as a filesystem path or a JavaScript property expression.

Do not fix the message without checking the path

A message such as “must be string” is incomplete on its own. In a nested array, the same keyword may fail at several locations. Log or display the instance path, schema path, keyword, and structured parameters together.

A repeatable JSON Schema debugging workflow

  1. Confirm the input is valid JSON. A parser error occurs before JSON Schema validation. Remove comments, trailing commas, undefined, and other JavaScript-only syntax.
  2. Confirm the active dialect. Read $schema and ensure the validator supports and is configured for that dialect.
  3. Validate the schema itself. Catch misspelled keywords, invalid keyword values, and unsupported vocabulary before validating instances.
  4. Follow the instance path. Inspect the exact runtime value, including its type and object level.
  5. Follow the schema path. Determine which subschema and keyword evaluated that value.
  6. Find the deepest useful errors. A leaf type or required failure is often more actionable than the parent oneOfsummary.
  7. Reduce the pair. Keep one failing instance value and only the schema branches required to reproduce the failure.
  8. Decide which side is wrong. Fix producer data when the contract is right; change the schema only when the contract itself is wrong or incomplete.
  9. Add a regression case. Test the failing value, the corrected value, and a nearby invalid value so the repair does not silently weaken the contract.

Error 1: required property is missing

The required keyword applies to an object and lists property names that must exist directly on that object. Defining a name under properties does not make it required. Conversely, a property can be required without having a schema under properties.

// Schema
{
  "type": "object",
  "required": ["name"],
  "properties": {
    "name": { "type": "string", "minLength": 1 }
  }
}

// Invalid instance
{ "displayName": "Ada" }

The fix is not automatically to remove name from required. Check the contract. If producers are supposed to send name, fix the payload or its serialization mapping. If displayName is the intended API field, update the schema and every consumer deliberately.

Why the property looks present but still fails

  • It is nested at the wrong level: /profile/name is not the same as root /name.
  • Its capitalization differs: userId and userID are different names.
  • A serializer omitted a value such as JavaScript undefined before producing JSON.
  • You are reading the schema path instead of the instance path. The property exists in the contract, not in the submitted data.

Presence and value are separate constraints. { "name": null } satisfies the presence requirement, but fails { "type": "string" }. If null is intentional, model it explicitly with { "type": ["string", "null"] }or an equivalent union for the dialect and platform you use.

Error 2: type mismatch

JSON Schema validates JSON types, not the type a UI control or TypeScript interface claims to have. A quoted number is a string. false is a boolean, not the string "false". null is its own JSON type.

// Schema
{
  "type": "object",
  "properties": {
    "age": { "type": "integer", "minimum": 0 },
    "subscribed": { "type": "boolean" }
  },
  "required": ["age", "subscribed"]
}

// Invalid
{ "age": "42", "subscribed": "false" }

// Valid
{ "age": 42, "subscribed": false }

Fix coercion at the boundary where strings enter the system, such as form parsing, environment-variable loading, CSV ingestion, or query parameters. Some validators offer non-standard coercion or data-mutation options. Those can be useful, but they change the instance-validation pipeline and may hide producer bugs. Test the post-validation value if you enable them.

Error 3: additionalProperties rejects an unexpected field

Setting additionalProperties: false closes an object against names not covered by properties or patternProperties in that schema object. This catches typos and undocumented fields, but it also makes schema evolution and composition less forgiving.

// Schema
{
  "type": "object",
  "properties": {
    "id": { "type": "string" },
    "email": { "type": "string" }
  },
  "additionalProperties": false
}

// Invalid: "display_name" is not declared
{
  "id": "usr_123",
  "email": "[email protected]",
  "display_name": "Ada"
}

Inspect the error's structured parameters for the rejected name. Then choose among three intentional fixes: remove or rename the producer field, declare it under properties, or allow a controlled family of keys with patternProperties. Removing additionalProperties: false globally is usually too broad if closed objects are part of the API contract.

The allOf composition trap

A common surprise is declaring base properties in one allOf branch and addingadditionalProperties: false in another. The keyword does not automatically treat properties declared in sibling subschemas as local declarations. The official JSON Schema object guide documents this closed-schema extension problem.

{
  "allOf": [
    {
      "type": "object",
      "properties": { "id": { "type": "string" } },
      "required": ["id"]
    }
  ],
  "properties": { "name": { "type": "string" } },
  "additionalProperties": false
}

Depending on the intended dialect, either redeclare the allowed properties where additionalProperties is applied or use unevaluatedProperties: falsein Draft 2019-09 or 2020-12. unevaluatedProperties can account for properties successfully evaluated by applicable subschemas, which makes it better suited to many composed closed-object contracts.

Error 4: enum, const, pattern, length, or numeric constraint fails

Once the runtime type is correct, leaf constraints define the allowed value. These errors are usually straightforward, but small assumptions cause recurring bugs:

  • enum compares against the listed JSON values; capitalization and type matter.
  • const requires one exact JSON value and is useful as a branch tag.
  • pattern uses a regular expression and is not implicitly anchored. Add ^ and $ when the whole string must match.
  • minLength and maxLength apply to strings, not arrays or object property counts.
  • minimum and maximum are inclusive; use exclusiveMinimum or exclusiveMaximum for strict bounds in modern drafts.
{
  "type": "object",
  "properties": {
    "status": { "enum": ["pending", "active", "disabled"] },
    "code": { "type": "string", "pattern": "^[A-Z]{3}-[0-9]{4}$" },
    "score": { "type": "number", "minimum": 0, "maximum": 100 }
  }
}

Error 5: format appears inconsistent

format is one of the most implementation-sensitive areas of JSON Schema. In Draft 2020-12, the standard meta-schema uses the format-annotation vocabulary; validators may offer assertion behavior separately. Libraries also differ in bundled formats, plugins, and strictness. Ajv commonly uses the separate ajv-formats package for formats such as date-time, uri, and email.

When a format is ignored or unexpectedly rejected, check all four layers:

  1. The declared JSON Schema dialect and vocabulary.
  2. The validator version and dialect class or mode.
  3. Whether format assertion is enabled.
  4. Whether the relevant built-in or plugin format is installed and registered.

Format is not a substitute for business validation

An email-format checker can evaluate syntax; it cannot prove that a mailbox exists, is deliverable, or belongs to the user. A date-time format can check representation without deciding whether that time is acceptable for your application.

Error 6: oneOf, anyOf, or allOf fails

Composition keywords create parent errors with important child errors underneath. Their logic is precise:

KeywordValid whenCommon failure
allOfEvery subschema validates.Two branches impose conflicting constraints.
anyOfOne or more subschemas validate.No branch matches.
oneOfExactly one subschema validates.No branch matches, or multiple branches match.

Why oneOf often matches more than one branch

Object schemas are permissive unless you require discriminating properties or close the object. Two branches that only describe optional properties may both accept an empty or generic object. Make the alternatives distinguishable with a required tag and const value:

{
  "oneOf": [
    {
      "properties": {
        "kind": { "const": "card" },
        "last4": { "type": "string", "pattern": "^[0-9]{4}$" }
      },
      "required": ["kind", "last4"]
    },
    {
      "properties": {
        "kind": { "const": "bank" },
        "iban": { "type": "string", "minLength": 15 }
      },
      "required": ["kind", "iban"]
    }
  ]
}

When no branch matches, group child errors by branch and find the smallest set of leaf failures. When multiple branches match, look for missing exclusivity rather than trying to “fix” every successful branch.

Error 7: if, then, and else apply unexpectedly

The if schema does not make its test properties mandatory unless you say so. A condition that only contains properties can pass when the tested property is absent, because properties constrains a property when it exists. Add required inside if when presence is part of the condition.

{
  "if": {
    "properties": { "country": { "const": "US" } },
    "required": ["country"]
  },
  "then": {
    "properties": { "postalCode": { "pattern": "^[0-9]{5}(-[0-9]{4})?$" } },
    "required": ["postalCode"]
  }
}

Also remember that if does not directly make the instance valid or invalid; it selects whether then or elseapplies. Inspect the selected branch's leaf errors for the actionable failure.

Error 8: $ref cannot be resolved

A $ref is a URI reference. Its target depends on the current base URI, which is influenced by $id. A relative reference that works when a schema is loaded from one file or URL may fail when the same schema is passed to a validator as an anonymous in-memory object.

// root schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://schemas.example.com/order.json",
  "$defs": {
    "money": {
      "type": "object",
      "properties": {
        "amount": { "type": "number" },
        "currency": { "type": "string" }
      },
      "required": ["amount", "currency"]
    }
  },
  "properties": {
    "total": { "$ref": "#/$defs/money" }
  }
}

For local references, verify the fragment and JSON Pointer escaping. For external references, verify the resolved absolute URI, ensure the target schema is registered with the validator or permitted to load, and check redirects, authentication, network policy, and content type. Do not make runtime production validation depend casually on an unreliable remote fetch; bundle or register trusted schemas where practical.

Error 9: the schema uses the wrong draft

JSON Schema releases are dialects with different keywords and semantics. The official dialect declaration guide describes $schemaas the declaration of the dialect in which a schema is written. If it is missing, your validator may apply a configured default that is not the author's intent.

Common migration mismatches include:

  • Draft 2020-12 tuple validation uses prefixItems, with items for the remaining elements, rather than the older array form of items.
  • Modern drafts use $defs; older schemas often use definitions.
  • The older dependencies keyword was split into dependentRequired and dependentSchemas in newer drafts.
  • unevaluatedProperties is available in Draft 2019-09 and later, not draft-07.
  • OpenAPI 3.0's schema dialect is not identical to full JSON Schema; OpenAPI 3.1 aligns its Schema Object with the JSON Schema 2020-12 family while retaining specification rules around its base dialect.

Do not “fix” an unknown keyword by deleting it until you know whether the schema or validator is on the wrong dialect. For OpenAPI-specific cases, use the OpenAPI 3.1 validation errors guide.

Quick diagnosis table

Keyword or symptomLikely causeFirst fix to test
requiredProperty absent at the reported object path.Fix the field name, nesting, or producer mapping.
typeRuntime JSON type differs from the contract.Parse at the boundary or correct the declared type.
additionalPropertiesUndeclared name, typo, or composed closed schema.Inspect the extra name and the schema object that closes it.
oneOfZero or multiple alternatives validate.Inspect branch errors; add a required const discriminator.
formatAssertion support or format plugin differs.Verify dialect, format registration, and validator options.
Unresolved $refWrong base URI, fragment, registration, or retrieval policy.Resolve against $id and verify the absolute target.
Unknown keywordMisspelling, unsupported draft, or custom vocabulary.Validate the schema against its declared meta-schema.

Worked example: from noisy errors to one root cause

Consider a webhook payload that fails several rules at once:

// Instance
{
  "eventType": "payment.created",
  "data": {
    "id": 90210,
    "amount": "49.95",
    "curreny": "USD"
  }
}
// Relevant schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["eventType", "data"],
  "properties": {
    "eventType": { "const": "payment.created" },
    "data": {
      "type": "object",
      "required": ["id", "amount", "currency"],
      "properties": {
        "id": { "type": "string" },
        "amount": { "type": "number", "exclusiveMinimum": 0 },
        "currency": { "type": "string", "pattern": "^[A-Z]{3}$" }
      },
      "additionalProperties": false
    }
  }
}

The useful failures are:

  • /data/id fails type: the producer sent a number instead of a string.
  • /data/amount fails type: the decimal was serialized as a string.
  • /data fails required for currency.
  • /data fails additionalProperties for curreny.

The last two errors describe one typo, not two independent business failures. Renaming curreny to currency resolves both. The corrected instance is:

{
  "eventType": "payment.created",
  "data": {
    "id": "90210",
    "amount": 49.95,
    "currency": "USD"
  }
}

This is why error count is not root-cause count. Group errors by instance location and rejected property before displaying them to developers or API consumers.

Ajv example: preserve structured error details

In JavaScript or TypeScript, do not flatten validation failures into one generic string too early. Ajv exposes structured errors on the compiled validation function. The exact options and supported drafts depend on the Ajv class and version, so configure the dialect deliberately.

import Ajv2020 from "ajv/dist/2020.js";

const ajv = new Ajv2020({
  allErrors: true,
  strict: true,
});

const validate = ajv.compile(schema);
const valid = validate(data);

if (!valid) {
  const errors = (validate.errors ?? []).map((error) => ({
    instancePath: error.instancePath,
    schemaPath: error.schemaPath,
    keyword: error.keyword,
    params: error.params,
    message: error.message,
  }));

  console.error(errors);
}

allErrors: true can improve debugging because evaluation continues after the first failure, but it may produce many related errors and adds work. Use it deliberately for untrusted or very large inputs, and still group the result into root causes before showing it to users. Ajv strict mode is also valuable because it can reject or warn about ambiguous and ignored schema constructs without changing JSON Schema validation results.

How to design useful validation errors for an API

Raw validator output is for debugging, not automatically a stable public API. Normalize it into a documented error shape while preserving the original structured details in logs.

{
  "code": "VALIDATION_FAILED",
  "errors": [
    {
      "path": "/data/amount",
      "rule": "type",
      "message": "Expected a number.",
      "expected": "number"
    }
  ]
}
  • Return instance paths that API consumers can map back to their payload.
  • Keep machine-readable codes and rule names stable.
  • Do not expose internal schema URLs, filesystem locations, secrets, or remote-fetch details in public responses.
  • Avoid echoing sensitive submitted values. A path and expected constraint are often enough.
  • Preserve correlation IDs and full structured errors in protected observability systems.

Production checklist

  1. Declare $schema explicitly.
  2. Give reusable schemas stable, absolute $id values.
  3. Validate schemas against the correct meta-schema in CI.
  4. Run positive, negative, boundary, and regression instances.
  5. Test every oneOf branch and a value that must match none.
  6. Test that oneOf branches cannot match simultaneously.
  7. Register or bundle trusted referenced schemas predictably.
  8. Pin validator and format-plugin versions.
  9. Document any non-standard coercion, defaults, or property removal.
  10. Return normalized errors without leaking submitted secrets.

Use CodeAva tools to isolate the failure

Start in the JSON Schema Validator & Generator to validate the instance against the intended schema and compare the failing keyword with its instance path. If the input is difficult to inspect, normalize it first with the JSON Formatter.

For deeply nested instances, use the jq, JSONPath & XPath Workbench to isolate the object or array item at the reported location. Query languages and JSON Pointer are not interchangeable, but isolating the same subtree makes a minimal failing example easier to build.

Do not weaken the schema just to make the error disappear

Removing required, adding every field to a type union, or allowing all additional properties may make one payload pass while silently breaking the contract. Decide whether the instance or the schema is wrong, then add a regression test for that decision.

Final debugging rule

A JSON Schema error becomes actionable when you can say: “this value, at this instance path, failed this keyword, in this schema dialect.” If one of those pieces is missing, collect it before changing the contract.

Start with the deepest required, type, or value-constraint errors; treat oneOf, anyOf, and conditional errors as branch summaries; and verify draft and base-URI behavior before rewriting references. Then reduce the failure, fix the correct side, and keep the minimal pair as a test.

Sources and further reading

#JSON Schema validation errors#JSON Schema error messages#JSON Schema required property#JSON Schema type mismatch#JSON Schema additionalProperties#JSON Schema oneOf error#JSON Schema format validation#JSON Schema $ref resolution#JSON Pointer#Ajv validation errors

Frequently asked questions

More from Kuda Zafevere

Found this useful?

Share:

Want to audit your own project?

These articles are written by the same engineers who built CodeAva\u2019s audit engine.