Reject answers with missing, absent, or mistyped values instead of reading them as zero - #3
Merged
Conversation
…g them as zero A noul answer with no value deserialized as 0.0, a choice with no choice as null, and so on, because the record components are primitives and Jackson defaults a missing primitive. In a fraud check that reads as "not fraud". Each answer record now has a JsonCreator taking boxed values and rejecting a missing or null required field, so the existing parse error path turns it into a TypeSafeException from systemOne. Parse errors also carry the JSON path so the message names the question (answers -> is_fraud). Fixes #1 (first half; the accessor exception type is left as is).
A question with no answer in the response only surfaced when the caller read that key, as an IllegalArgumentException, so systemOne returned normally and a catch of TypeSafeException never saw it. systemOne now diffs the question ids it sent against the answers it got back and throws TypeSafeException naming the unanswered ones, next to the existing null-answers check. One existing test asked question 'q' while the canned stub answered is_phishing/spam_category/urgency; its subject is the request's model field, so the question id now matches the fixture.
A noul question answered with a choice parsed fine and only failed when the caller read it, as an IllegalArgumentException indistinguishable from using the wrong accessor by mistake. The request knows what each question was, so the mismatch is detectable where the response is checked. systemOne now compares each question's type against its answer's and throws TypeSafeException naming the mismatched ids. Reading a good answer with the wrong accessor is still IllegalArgumentException: that is a caller bug, not a bad response.
garretpremo
approved these changes
Sep 19, 2026
garretpremo
added a commit
that referenced
this pull request
Sep 19, 2026
A response missing an answer, an answer field, or answering with a different type than asked now fails systemOne with a TypeSafeException instead of reading as zero or surfacing later (#1, #3). RequestOptions.maxRetries applies on top of the client's policy instead of replacing it with the default (#2). READMEs point at 0.2.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation:
Two defects from #1, both letting a malformed response pass as a good one.
0.0, since the record components are primitives.{"is_fraud":{"type":"noul"}}parses andnoul("is_fraud")returns0.0.IllegalArgumentException, socatch (TypeSafeException e)misses it.Changes:
@JsonCreatorover boxed values, rejecting missing or null required fields.legendstays optional.deserialize: includegetPathReference()so the message names the question.systemOne: diff the question ids sent against the answers returned, and compare each answer's type to its question's.systemOneKeepsAnExplicitModelaskedqagainst a stub answering other ids; fixture now matches.Result:
6 new tests, all failing on main with "nothing was thrown". 35 pass. Both defects now throw
TypeSafeExceptionfromsystemOne:Verified against live jev-1.13.0. Matches the Python SDK, which declares these fields required under
strict=True.Addresses: #1