Skip to content

oneOf schema-validator over-reports "must have required property" from non-matching variants — storyboard CI sees false negatives that local doesn't #1111

Description

@bokelley

Context

When running the storyboard CLI (npx -y -p @adcp/client@5.21.1 adcp storyboard run) against a server that returns AdCP 3.0.1 get_products responses, validation fails in CI but passes locally — same code, same client version, same Python version.

The downstream Python SDK has confirmed the response shapes are 3.0.1-compliant (Python jsonschema validates them clean). Tracking issue with full investigation: adcontextprotocol/adcp-client-python#324.

Symptoms

Repeated stderr from @adcp/client/dist/lib/core/TaskExecutor.js:1119:

Schema validation failed for get_products: [
  "/products/2/name: must have required property 'name'",
  "/products/2/description: must have required property 'description'",
  "/products/2/publisher_properties: must have required property 'publisher_properties'",
  "/products/2/reporting_capabilities: must have required property 'reporting_capabilities'",
  "/products/2/format_ids/0/agent_url: must have required property 'agent_url'",
  ...
]

The dumped response body in the same log shows products[2] does have all those fields populated. Validator reports them missing anyway.

Likely root cause

The 3.0.1 Product schema is a oneOf discriminated union (per delivery_type / pricing_options[*].pricing_model). It looks like the client's Ajv setup iterates each oneOf variant and surfaces must have required property errors from variants that didn't match — even when one variant DID match and overall validation should pass.

Suggested fix: configure Ajv with allErrors: false for oneOf variant errors, OR collect + filter so only the best-matching-variant's residual errors surface, OR check if any variant validates and short-circuit before emitting the cross-variant error union.

Source under suspicion: @adcp/client/dist/lib/validation/index.js (Ajv setup) and dist/lib/core/TaskExecutor.js:1119 (validation-failure logger).

Reproduction

git clone https://github.com/adcontextprotocol/adcp-client-python
cd adcp-client-python
python3 -m venv .venv && .venv/bin/pip install -e .[dev]
ADCP_PORT=3001 .venv/bin/python examples/seller_agent.py &
npx -y -p @adcp/client@5.21.1 adcp storyboard run http://127.0.0.1:3001/mcp media_buy_seller --json --allow-http

Local: overall_status: passing (45/47 pass).
Fresh CI install: overall_status: partial, ~13 storyboards fail with the validation errors above.

The structural difference between passing products (indices 0, 1) and failing products (2-5 — injected via seed_product controller scenarios) is the pricing_option_id value (po-cpm-homepage vs cpm_standard) and product_id (hyphenated vs underscored). Neither has a schema-level pattern restriction visible in the cache, so the divergence likely originates in the validator's error-aggregation logic rather than in the schemas themselves.

Why this matters downstream

The Python SDK's storyboard CI job is continue-on-error: true because of this divergence — but it makes every PR (e.g., adcontextprotocol/adcp-client-python#323) ship with one red check that's purely reporter-side, masking any real future regression. A fix here un-masks the signal.

References

  • Downstream tracking: adcontextprotocol/adcp-client-python#324
  • Python-side regression anchor: adcontextprotocol/adcp-client-python#327 (confirmed all six static products validate clean against AdCP 3.0.1 via Python jsonschema)
  • Source of the validation-failure log: @adcp/client/dist/lib/core/TaskExecutor.js:1119

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions