Skip to content

Map HTTP failures to real exceptions, and make DRI lookups honest#11

Merged
mcgarrah merged 1 commit into
mainfrom
claude/error-codes-and-dri
Jul 13, 2026
Merged

Map HTTP failures to real exceptions, and make DRI lookups honest#11
mcgarrah merged 1 commit into
mainfrom
claude/error-codes-and-dri

Conversation

@mcgarrah

Copy link
Copy Markdown
Owner

The follow-up to v0.1.11 (issues #4 and #5 from the review), plus one more bug found on the way. Same theme throughout: APIs that were documented but never implemented, and numbers compared against the wrong scale.

Verified against the live FDC API. The 30 new tests do not even import on main — the functions and exports they use don't exist there.

Errors

status_code never existed. docs/user/error_handling.rst tells callers to branch on e.status_code, and ships a retry-on-5xx example built around it. Nothing ever set the attribute, so hasattr(e, 'status_code') was always False and that retry loop could never fire. Every FdcApiError now carries it, and it is None for failures that never reached the API (refused connection, timeout) — itself worth knowing.

An invalid API key was a mystery. FDC sits behind api.data.gov, which rejects a bad key with 403, not 401 — and only 401 was mapped. So the single most common mistake a caller can make surfaced as a nondescript FdcApiError. Confirmed against the live API:

request HTTP before after
bad API key 403 FdcApiError FdcAuthError
missing food 404 FdcApiError FdcResourceNotFoundError
page_size=99999 400 FdcApiError FdcValidationError

FdcResourceNotFoundError and FdcValidationError had been defined, documented, and raised by nothing. They are now also exported from the package root, which they never were. Everything still derives from FdcApiError, so a broad except FdcApiError keeps working.

DRI

Only DriType.RDA ever returned data. ul.json ships real Tolerable Upper Intake Levels, but under a different schema than get_dri knew how to read, so every UL lookup came back None. Both schemas are now understood. AI, EAR and AMDR ship no data at all — they still return None, but log a warning once saying so, rather than leaving an unexplained empty column. The docs claimed data for all five types; they now say which two are real.

DRI percentages ignored units — and the data does not use one scale. Iron's RDA is 8 mg; its UL is 0.045 g. The percentage was a bare division, so 100g of spinach measured against iron's upper limit reported:

before:  1.261 mg / 0.045      = 2802.2%   of the upper limit
after:   1.261 mg / 45 mg      =    2.8%

Amounts are now converted into the DRI's own unit first. Where the two cannot be compared at all — vitamin A in IU against a µg allowance — dri_percent is None rather than a confident wrong number. New get_dri_value returns the unit alongside the value so this can't be got wrong again; get_dri still returns the bare number for existing callers.

Tests

30 new tests, in tests/unit/test_error_mapping.py and tests/unit/test_dri.py:

  • every status maps to its exception; status_code is carried on all of them and is None when the request never reached the API; a bad key is an auth error; the documented 5xx retry is actually reachable; everything is still catchable as FdcApiError.
  • UL lookups return data; UL applies to both genders; a type with no data warns once and returns None; RDA percentages use the RDA's unit; a gram-denominated UL is not compared against milligrams; IU yields no percentage; get_dri still returns a bare number; age/gender still select the right RDA (iron: 18 mg for women 19–50, 8 mg after).

Full suite: 87 passed, 8 skipped. Both docs examples I touched were executed rather than assumed.

🤖 Generated with Claude Code

https://claude.ai/code/session_014avzoF5VnrUxwoy7m1aK6K

The follow-up to the v0.1.11 fixes. Same theme: APIs that were documented
but not implemented, and comparisons made against the wrong scale.

Errors:
- FdcApiError and its subclasses now carry status_code. The docs have long
  told callers to branch on it, with a retry-on-5xx example that could
  never have run: nothing set the attribute, so hasattr() was always False.
- An invalid API key raised a nondescript FdcApiError. FDC sits behind
  api.data.gov, which answers a bad key with 403 while only 401 was mapped,
  so the commonest caller mistake missed FdcAuthError entirely.
- 404 now raises FdcResourceNotFoundError and 400 raises FdcValidationError.
  Both classes were defined, documented, and raised by nothing. A food that
  does not exist is an ordinary outcome of a lookup, not a breakdown.

DRI:
- Only DriType.RDA ever returned data. ul.json ships real upper limits under
  a different schema than get_dri could read, so every UL lookup was None.
  Both schemas are now understood. AI/EAR/AMDR ship no data and still return
  None, but say so once in a warning instead of leaving the column blank.
- DRI percentages ignored units, and the data does not use one scale: iron's
  RDA is 8 mg, its UL is 0.045 g. Spinach against iron's upper limit reported
  2802% of it rather than 2.8%. Amounts are now converted into the DRI's unit
  first, and an incomparable pair (vitamin A in IU against a µg allowance)
  yields no percentage instead of a confident wrong one.

Verified against the live API; the 30 new tests do not even import on main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014avzoF5VnrUxwoy7m1aK6K
@mcgarrah
mcgarrah merged commit 1388ef0 into main Jul 13, 2026
4 checks passed
@mcgarrah
mcgarrah deleted the claude/error-codes-and-dri branch July 13, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants