Skip to content

Precompiled validators miss sub-schemas under patternProperties and additionalProperties #5373

Closed

Description

@heath-freenome

Prerequisites

What theme are you using?

validator-ajv8

Version

v7 branch (also affects @rjsf/validator-ata)

Current Behavior

A precompiled validator throws No precompiled validator function was found for the given schema when a oneOf/anyOf is reachable only through a patternProperties or additionalProperties value.

schemaParser() collects the sub-schemas that isValid() will be called with, and compileSchemaValidators()/compileSchemaValidatorsCode() precompile a validator for each of them by hash. parseSchema() only recurses into a schema's properties and non-tuple items, so the values of patternProperties and additionalProperties are never parsed on their own. At runtime, stubExistingAdditionalProperties() gives each extra key in the form data its patternProperties/additionalProperties schema, and computing that key's defaults or rendering its MultiSchemaField calls isValid() on the options, whose hashes are missing from the precompiled map.

Found by the code review of #5371 (the #5338 fix), which only covers a patternProperties entry that matches a named property, because that one is merged into the property.

Expected Behavior

Every sub-schema the form can validate against has a precompiled function, including those under patternProperties and additionalProperties.

Steps To Reproduce

import type { RJSFSchema } from '@rjsf/utils';
import { getDefaultFormState, schemaParser } from '@rjsf/utils';
import { compileSchemaValidatorsCode } from '@rjsf/validator-ajv8/compileSchemaValidators';
import { createPrecompiledValidator } from '@rjsf/validator-ajv8';

const choice: RJSFSchema = { oneOf: [{ const: 'a' }, { const: 'b' }] };
// Either one reproduces it
const rootSchema: RJSFSchema = { type: 'object', patternProperties: { '^x_': choice } };
// const rootSchema: RJSFSchema = { type: 'object', additionalProperties: choice };

// false: the options were never parsed
Object.values(schemaParser(rootSchema)).some((s) => s.const === 'a');

const validator = createPrecompiledValidator(loadModule(compileSchemaValidatorsCode(rootSchema)), rootSchema);
// Error: No precompiled validator function was found for the given schema for "-4c020a92"
getDefaultFormState({ validator }, { schema: rootSchema, rootSchema, formData: { x_1: 'b' } });

(loadModule evaluates the generated module code, as test/harness/compileSuperSchema.ts#evalValidatorCode does.)

Both variants were confirmed against the fix-5338 branch.

Environment

- **OS**: macOS (Darwin 25.6.0)
- **Node**: 24.21.0

Anything else?

A likely fix is for parseSchema() to also recurse into each patternProperties value and an object-valued additionalProperties, the way it does for properties; recurseList already skips schemas it has seen. The same probably applies to tuple items and additionalItems, which it also skips today.

Activity

  1. added
    needs triageInitial label given, to be assigned correct labels and assigned
    and removed
    needs triageInitial label given, to be assigned correct labels and assigned
    on Sep 28, 2026
  2. heath-freenome commented on Oct 3, 2026

    @heath-freenome
    MemberAuthor

    Fixed by #5371, in 36aae2838 — dated three days after this was filed, which is why it reproduced against fix-5338 at the time.

    That commit rewrote the parser's recursion into parseValueSchemas(), which walks properties, additionalProperties, every combination of patternProperties, every position of a tuple items, and additionalItems. So the "same probably applies to tuple items and additionalItems" note is covered too.

    patternProperties isn't walked as the patterns themselves but as { allOf: [...matching] } for each subset of them, because a form renders a key with the merge of every pattern it matches and a parse has no form data to say which key matches which. There are 2^n - 1 subsets, so a schema with more than 16 patternProperties is now reported with a message saying why rather than parsed for minutes.

    Verification

    I ran this issue's repro at the v7 base (320152a9f) and at the PR head (330a8eb53), swapping only the four changed @rjsf/utils source files so nothing else varied.

    Variant v7 base PR head
    patternProperties: { '^x_': choice } options not parsed; getDefaultFormState() throws No precompiled validator function was found for the given schema for "-4c020a92" options parsed; returns {"x_1":"b"}
    additionalProperties: choice options not parsed; throws the same error on the same hash options parsed; returns {"x_1":"b"}, scoring returns option 1

    One correction to the report: the additionalProperties variant does not throw through getDefaultFormState() at the base — that path returns {"x_1":"b"} without scoring the options. It throws one step later, where MultiSchemaField would: retrieving the stubbed x_1 and scoring its retrieved options raises the identical error on the identical hash. The sub-schemas are missing from the compiled map either way, which is what the issue is about; only the triggering call differs.

    Since schemaParser() lives in @rjsf/utils, @rjsf/validator-ata is fixed by the same change.

    Locked in by two tests in packages/utils/test/parser/schemaParser.test.ts — parses the options of the additionalProperties and patternProperties a form renders extra keys with, which is this issue's schema almost verbatim, and parses the options of every position of a tuple items and of its additionalItems.

  3. heath-freenome commented on Oct 7, 2026

    @heath-freenome
    MemberAuthor

    Fixed in the upcoming v7 release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions