Skip to content

Report property name as found in input in deserialization error paths - #368

Open
cowtowncoder wants to merge 9 commits into
3.xfrom
tatu-claude/3.x/alias-name-in-error-path
Open

cowtowncoder wants to merge 9 commits into
3.xfrom
tatu-claude/3.x/alias-name-in-error-path

Conversation

@cowtowncoder

@cowtowncoder cowtowncoder commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Summary

Companion to FasterXML/jackson-databind#6364 (now merged), which makes stock deserializers report a failing property under the name found in input (alias, or other case with case-insensitive matching). Without this PR, the same input gives a different error path depending on whether Afterburner or Blackbird is used.

Needs a jackson-databind snapshot with #6364 and #6373 (build 154 or later): Afterburner and Blackbird both use the new BeanDeserializerBase._failedPropertyName(), and Blackbird's BBReaderOddTokenTest expects stock to report the token that #6373 covers.

Afterburner

  • _deserializeDisordered() reports the input name. As in Blackbird, it only reads that name for beans with aliases or case-insensitive matching; it reads it at the value token, as stock does, and goes through _failedPropertyName().
  • Separate, older bug: SuperSonicBeanDeserializer called _deserializeDisordered() inside the try of its fast path, so a failure there was wrapped twice. For a bean with more than 6 properties, {"a":"bad"} where a is not the first ordered property, or any alias, got a bogus extra path segment such as ["a","a1"]. Those calls are now outside the try.
  • Reading of property names and value tokens is also out of those try blocks, in both SuperSonicBeanDeserializer and SuperSonicUnrolledDeserializer. {"a":1, bad used to blame a, which had parsed successfully. Each property keeps its own deserializeAndSet() call site, for type profiles.
  • Separate, older bug: SuperSonicBeanDeserializer.deserialize(p, ctxt, bean) failed on a token that is neither START_OBJECT nor PROPERTY_NAME, for example readerForUpdating(bean).readValue("12") or a @JsonMerge property with a scalar value. It now leaves the bean as is, same as stock and SuperSonicUnrolledDeserializer.
  • Removed the deserializeFromObject() overrides of both deserializers. Their fast path needed vanilla processing, but databind only calls this method for non-vanilla deserializers, for which they just delegated to super. With the fast path made to throw, only a test calling it directly reached it.

Behavior changes, both matching stock:

  • Parser failures now have an empty path, where the fast paths used to report a property.
  • Non-Jackson exceptions from handling of unknown properties, such as an IllegalStateException thrown by a DeserializationProblemHandler, are no longer wrapped in DatabindException for beans with more than 6 properties.

One more change, for external code calling deserializeFromObject() directly on these deserializers: at START_OBJECT it now returns an empty bean, as the stock method does, instead of reading properties. Databind advances the parser before calling this method, and nothing in databind or this repo makes such a call.

Blackbird

  • POJO and record codecs now read the input name, as builder codecs did, and only for beans with aliases or case-insensitive matching. Other beans pay nothing.
  • Builder codecs no longer need startObject or view checks, since stock now reports the input name on all its loops. That removes the extra local and the _failedName() rules.
  • Follows jackson-databind#6373 (partial fix of #6357): generated POJO codecs now report a token that is neither a name nor END_OBJECT after an unknown property, instead of returning the bean. This also fixes the BBReaderOddTokenTest failure that the databind rebuild on 3.x has. JSON input cannot produce such a token; a TokenBuffer or non-JSON parser can. Removes the unknown-name tracking and the vanillaLoop plan flag. Record codecs still end their loop on such a token, as stock's property-based loop does until the rest of #6357 is fixed.

Testing

  • Against published databind snapshot build 154: ./mvnw verify -pl afterburner,afterburner-tests passes with JDK 17, and -pl blackbird,blackbird-tests,blackbird-jpms-tests -Dblackbird.test.oldJdks=... with JDK 25, plus 17 and 21 for the old-JDK runtime tests. CI covers all modules and JDKs.
  • No benchmarks were run.
  • Earlier commit, before the follow-up fixes: ./mvnw verify on all of jackson-modules-base with JDK 25 passed against jackson-databind#6364 installed into a scratch copy of the local Maven repository.
  • AliasErrorPathTest covers both optimized deserializers (with DEFAULT_VIEW_INCLUSION enabled, which they need), including aliases, case-insensitive matching, structured values, unknown properties, problem handler failures, parser failures and update from a non-Object token. A stack frame check confirms the update-from-property-name route.
  • UnknownProperty343Test now has vanilla and update cases for a trailing unknown property, which reach Afterburner's own code. Breaking that handling or the update-from-non-Object fix in a scratch copy makes the new tests fail.
  • Not covered directly: when the input name is read in _deserializeDisordered(), since JSON gives the same result either way.
  • New BBCodecStockChecksTest cases compare POJO and record failures against stock, with default settings, vanilla settings and case-insensitive matching, for structured values (child codecs, stock reads, field stores, failing setters) and under an active view. Removing the input name read from the codegen in a scratch copy makes them fail.
  • Not covered: the inputName debug name of record codecs, as no test checks debug metadata.

🤖 Generated with Claude Code

cowtowncoder and others added 2 commits November 1, 2026 10:21
Companion to FasterXML/jackson-databind#6364: Afterburner and Blackbird
now report the same name as stock deserializers when a property value
fails. Also stops `SuperSonicBeanDeserializer` from wrapping failures of
`_deserializeDisordered()` twice, which added a bogus path segment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cowtowncoder and others added 7 commits November 1, 2026 10:50
Reading of property names and value tokens is no longer within `try` blocks
that blame a property, so parser failures have no path, as with stock
deserializers. Applies to both `SuperSonicBeanDeserializer` and
`SuperSonicUnrolledDeserializer`.

`_deserializeDisordered()` only reads the input name for beans with aliases
or case-insensitive matching, at the value token, and goes through
`_failedPropertyName()`, same as stock and Blackbird.

Tests now also cover `deserializeFromObject()` and update from a property
name, which `orderedFromPropertyName` did not reach, plus case-insensitive
matching, structured values, unknown properties and problem handler failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y codegen

`BBCodecStockChecksTest` now compares failures against stock for aliased
properties with structured values (child codecs, stock reads, field stores,
failing setters) and under an active view.

Codegen: drop the `Names` record for a plain slot that is negative when
unused, so record codecs only allocate it when needed, and name the
`inputName` local of record codecs in debug metadata, as for POJO codecs.

`AliasErrorPathTest` mapper now comes from the test base helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fast path of these overrides needs vanilla processing, but databind only
calls `deserializeFromObject()` for non-vanilla deserializers, for which the
overrides just delegated to `super`. Removes them from both
`SuperSonicBeanDeserializer` and `SuperSonicUnrolledDeserializer`, along with
the test that reached them through a direct call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow does

jackson-databind#6373 (partial fix of #6357) made stock's
`_vanillaDeserializeWithUnknown()` report a token that is neither a name nor
END_OBJECT, instead of returning the bean. Generated POJO codecs copied the
old behavior, so `BBReaderOddTokenTest` fails with databind snapshots that
have the change. Removes the tracking of unknown names from generated code,
and the `vanillaLoop` plan flag that was only used for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_readAndSet()` put all `deserializeAndSet()` calls on one call site, which
would mix the type profiles of all properties on Afterburner's hot path. Each
property has its own call site again, with parser calls still out of `try`.

`SuperSonicBeanDeserializer.deserialize(p, ctxt, bean)` failed on a token that
is neither START_OBJECT nor PROPERTY_NAME, for example with `readerForUpdating()`
or `@JsonMerge` and a scalar value. It now leaves the bean as is, same as stock
and `SuperSonicUnrolledDeserializer`.

`UnknownProperty343Test` only went through stock databind: adds vanilla and
update cases that reach Afterburner's own code. Minor `AliasErrorPathTest` cleanup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

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

Labels

3.3 afterburner blackbird Issue related to Blackbird module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant