On MCP, creating a media buy with an inline creative and an idempotency_key crashes while computing the idempotency canonical payload hash, and the internal crash is surfaced to the buyer as a correctable VALIDATION_ERROR — masking the real creative-validation rejection.
Observed (BDD BR-RULE-015 INV-6, per transport)
- a2a / rest: the request reaches creative validation; the missing
asset_type discriminator is rejected as CREATIVE_REJECTED. (The idempotency hash falls back to canonical_request_hash(req), whose model_dump(mode="json") serializes FormatId cleanly, so no crash.)
- mcp: the transport threads the raw wire payload, so
canonical_payload_hash(raw_wire_payload) runs over a payload containing a live FormatId object. rfc8785 cannot serialize it → CanonicalizationError (a ValueError subclass) → normalize_to_adcp_error maps it to AdCPValidationError. The wire carries error_code=VALIDATION_ERROR, recovery=correctable, message unsupported type: <class 'src.core.schemas._base.FormatId'> — an internal crash presented as a correctable buyer error, and the real creative-validation error never surfaces.
Trace
src/core/tools/media_buy_create.py:2080 — canonical_payload_hash(raw_wire_payload) (idempotency-replay hash, when idempotency_key is set)
src/core/idempotency_canonical.py — canonical_payload_hash → rfc8785
src/core/exceptions.py (normalize_to_adcp_error) — isinstance(exc, ValueError) -> AdCPValidationError
- caught / logged by
src/core/tool_error_logging.py ("MCP boundary untyped CanonicalizationError")
Impact
- Buyer receives a misleading correctable
VALIDATION_ERROR for an internal serialization bug.
- Masks the real creative-validation rejection on MCP.
- Blocks graduation of the BR-RULE-015 INV-6 BDD scenario on MCP.
Suggested fix
Make the idempotency canonical hash tolerant of domain objects — serialize/coerce FormatId (and peers) in the raw wire payload before rfc8785, or hash the model-dumped form consistently across transports. Separately, avoid mapping an internal CanonicalizationError to a buyer-facing VALIDATION_ERROR.
Related
On MCP, creating a media buy with an inline creative and an
idempotency_keycrashes while computing the idempotency canonical payload hash, and the internal crash is surfaced to the buyer as a correctableVALIDATION_ERROR— masking the real creative-validation rejection.Observed (BDD BR-RULE-015 INV-6, per transport)
asset_typediscriminator is rejected asCREATIVE_REJECTED. (The idempotency hash falls back tocanonical_request_hash(req), whosemodel_dump(mode="json")serializesFormatIdcleanly, so no crash.)canonical_payload_hash(raw_wire_payload)runs over a payload containing a liveFormatIdobject.rfc8785cannot serialize it →CanonicalizationError(aValueErrorsubclass) →normalize_to_adcp_errormaps it toAdCPValidationError. The wire carrieserror_code=VALIDATION_ERROR,recovery=correctable, messageunsupported type: <class 'src.core.schemas._base.FormatId'>— an internal crash presented as a correctable buyer error, and the real creative-validation error never surfaces.Trace
src/core/tools/media_buy_create.py:2080—canonical_payload_hash(raw_wire_payload)(idempotency-replay hash, whenidempotency_keyis set)src/core/idempotency_canonical.py—canonical_payload_hash→rfc8785src/core/exceptions.py(normalize_to_adcp_error) —isinstance(exc, ValueError) -> AdCPValidationErrorsrc/core/tool_error_logging.py("MCP boundary untyped CanonicalizationError")Impact
VALIDATION_ERRORfor an internal serialization bug.Suggested fix
Make the idempotency canonical hash tolerant of domain objects — serialize/coerce
FormatId(and peers) in the raw wire payload beforerfc8785, or hash the model-dumped form consistently across transports. Separately, avoid mapping an internalCanonicalizationErrorto a buyer-facingVALIDATION_ERROR.Related