fix(errors): keep a fail-envelope sentence out of error.code (#145) - #148
Merged
Merged
Conversation
error_from_response copied a string data.error into error.code whatever it held. In the render_fail envelope it is either a machine code (sentence in data.message) or the sentence itself, as every fuel-surcharge 400/404 sends, so code-based branching saw a unique free-text string per request. data.error now becomes the code only when it is a snake-case token: upper-snake (VALIDATION_ERROR, INTERVAL_FLOOR, WATCH_LIMIT) or lower-snake (invalid_code, no_price_data, invalid_request, which api_validations.rb and prices_controller.rb send). A sentence stays the message and code is None. A canonical nested error object keeps precedence; redaction is unchanged. Closes #145 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Resolve the CHANGELOG ### Fixed conflict with #147 by keeping both entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo
karlwaldman
added a commit
that referenced
this pull request
Sep 13, 2026
Resolve the CHANGELOG ### Fixed conflict with #148 by keeping both entries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo
This was referenced Sep 13, 2026
Merged
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.
Summary
error_from_responsecopied any stringdata.errorintoerror.codeanderror.machine_code. In the API'srender_failenvelope,{"status": "fail", "data": {"error": ...}}, that field holds one of two things:data.message. Upper-snake examples:VALIDATION_ERROR,INTERVAL_FLOOR,WATCH_LIMIT. Lower-snake examples:invalid_code(api_validations.rb), andno_price_dataandinvalid_request(prices_controller.rb).V1::FuelSurchargeController400/404 does this, and so do the rig-count, well-permit and date-validationrender_failcallers.For the second shape,
error.codebecame a unique free-text string per request.Fix.
data.errorbecomes the code only when_is_machine_codeaccepts it: a snake-case token, all upper or all lower, ASCII alphanumerics joined by_. A sentence stays inerror.messageanderror.codeisNone.Unchanged:
errorobject, top-level or underdata, still takes precedence.messagebeside a machine code, is redacted frommessage,str(),raw_bodyandraw_text.Parity with oilpriceapi-node #114
Same precedence and the same code/message split, with two deliberate differences:
MACHINE_CODEis upper-snake only, so it dropsinvalid_code/no_price_data/invalid_requestfromerror.code. Those are real machine codes: liveGET /v1/prices/latest?by_code=NOT_A_REAL_CODE_XYZreturns{"error": "invalid_code", "message": "Code 'NOT_A_REAL_CODE_XYZ' not found. ..."}, andapi_validations.rbnotes clients parseerror. Before this PR, Python returnedcode == "invalid_code". Matching Node would regress that, so the gap is filed against Node instead as [P2][errors] lower-snake fail codes (invalid_code, no_price_data, invalid_request) are dropped from error.code oilpriceapi-node#117.datawhenstatusisfail/error. Python already readdatawithout it, and a sentence-shapeddata.erroris now refused either way, so behaviour did not need narrowing.Node's second #114 bug: absent field overwriting a subclass default code
Checked, not present in Python. No
OilPriceAPIErrorsubclass defines a defaultcode(Node hadNOT_FOUND_ERROR/HTTP_ERROR).codeis an explicit keyword on the base__init__, anderror_from_responsepassescommonkeys that no subclass defaults. The parametrized test over 400/401/402/403/404/422/429/503 pinscode is Nonefor each class on a sentence body.tests/unit/test_error_contract.pywas checked for pinned fixtures relying on the old fallback. Its fixtures usecode/error.codekeys, notdata.error, and all pass.Live fail bodies used as fixtures (api.oilpriceapi.com, 2026-09-13)
data.errorGET /v1/fuel-surcharge/nope/latest"Unknown carrier 'nope'. Covered carriers: odfl, ..."None, message = sentenceGET /v1/fuel-surcharge/parcel/ups/history"Parcel fuel-surcharge history requires a service_level parameter."NonePOST /v1/subscriptionswithcodes: []VALIDATION_ERROR, message"Codes can't be blank"VALIDATION_ERRORPOST /v1/subscriptionswithinterval_seconds: -5INTERVAL_FLOOR, message"Your plan's minimum snapshot interval is 60s ..."INTERVAL_FLOORGET /v1/prices/latest?by_code=NOT_A_REAL_CODE_XYZinvalid_code, message"Code 'NOT_A_REAL_CODE_XYZ' not found. ..."invalid_codeGET /v1/subscriptions/<unknown uuid>{"error": {"code": "NOT_FOUND", ...}}NOT_FOUNDA bad interval returns a 402
INTERVAL_FLOORupgrade trigger, not a 422. The live 422 came from emptycodes. No subscription was created by any probe; every create was refused.Red (tests written first, run on unchanged
origin/maina5304b3 source)Every failure is
codeholding the sentence:The 12 that pass on main are the regression guards: upper-snake and lower-snake codes, code-only body, canonical precedence, and redaction beside a machine code.
Green
Full suite. Baseline re-measured on
origin/maina5304b3 with all extras installed:origin/mainThe 3 failures are the live
tests/integration/test_demo_contract.py429s on both runs.ruff check oilpriceapi/: All checks passed!mypy oilpriceapi/ --ignore-missing-imports, in a CI-equivalent.[dev]environment: Success, no issues found in 55 source files.python scripts/validate_storefront_claims.py: validated 76 public surfaces.No local refusal was added, no pydantic validator, no raw
ValueError.Merge order
This PR and #147 (#142) both add entries under
## [Unreleased]/### FixedinCHANGELOG.md. Merge #147 first. This branch then takes a merge oforigin/main(no rebase, no force push) to resolve the CHANGELOG hunk.Closes #145
🤖 Generated with Claude Code
https://claude.ai/code/session_015ao5paex73xXvuM424Libo