Skip to content

Preserve 400 classification for malformed /api/upload bodies - #166

Merged
BigSimmo merged 3 commits into
mainfrom
copilot/preserve-client-error-status
Jul 2, 2026
Merged

Preserve 400 classification for malformed /api/upload bodies#166
BigSimmo merged 3 commits into
mainfrom
copilot/preserve-client-error-status

Conversation

CopilotAI commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • /api/upload was regressing malformed upload requests into generic 500s when request.formData() threw before file-field validation. This change restores 400 client-error behavior for invalid upload bodies and preserves 5xx classification for real backend failures.
  • Upload parse error handling
    • Wrap request.formData() failures as PublicApiError with status 400 (invalid_form_data).
    • Use default jsonError(error) in the route catch path so storage/database errors are not coerced to 400.
  • Contract tests
    • Added coverage for:
      • non-form upload content type
      • malformed multipart upload body
    • Both assert 400 and no storage/database side effects.
    • Updated coupled upload cleanup tests to assert 500 for document/job insert failures.
constformData=awaitrequest.formData().catch((cause)=>{thrownewPublicApiError("Invalid upload form data.",400,{code: "invalid_form_data",causeName: causeinstanceofError ? cause.name : null,causeMessage: causeinstanceofError ? cause.message : null,});});// ...returnjsonError(error);

Verification

  • npm run verify:cheap
  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed
  • npm run verify:release before release or handoff confidence claims
  • npm run format:check
  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed
  • npm run check:deployment-readiness when deployment startup, hosting, or rollout behavior changed

Clinical Governance Preflight

Complete this section when the change touches ingestion, answer generation, search/ranking, source rendering, document access, privacy, production env, or clinical output.

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • No schema, Supabase env, or deployment behavior changes.

CopilotAI changed the title [WIP] Preserve client-error status for malformed upload bodiesPreserve 400 classification for malformed /api/upload bodiesJul 2, 2026
CopilotAI requested a review from BigSimmoJuly 2, 2026 10:18
@BigSimmo
BigSimmo marked this pull request as ready for review July 2, 2026 10:27
@BigSimmo
BigSimmo enabled auto-merge July 2, 2026 10:27
@BigSimmo
BigSimmo merged commit 9dc81a6 into mainJul 2, 2026
4 checks passed
@BigSimmo
BigSimmo deleted the copilot/preserve-client-error-status branch July 2, 2026 16:31
BigSimmo pushed a commit that referenced this pull request Jul 31, 2026
Three conflicts, resolved by evidence rather than by side-preference.
scripts/ensure-codex-cloud-git-remote.mjs and tests/codex-cloud-setup.test.ts:
took main's version. The branch carried its own fix for the same defect
(5aa3d6c, judging origin safety from the configured remote URL) on top of
main's #1497, so neither side was obviously newer. main's version introduces a
configuredOriginUrl() helper, already covers the url.*.insteadOf case the
branch's extra test was written for, and passes 12/12 locally — verified
before choosing, since taking a side wholesale is only safe when the other
side's content is genuinely subsumed. This also clears the two
codex-cloud-setup failures this branch has carried all session.
docs/outstanding-issues.md: fifth id collision on this branch. main has since
claimed #151-#153 — the exact ids these rows previously held — and now runs to
#164. main's rows are kept and the follow-ups renumber to #165 (adopt a
consolidated answer-home notice block), #166 (answer mode ships no
verify-before-use caveat) and #167 (verify:pr-local exits 0 when its build
step refuses to run), with their three cross-references and the marker (168)
following.
check:outstanding-issues: 165 rows (50 open, 115 archived), unique ids,
next-id=168 above the highest, no ids deleted from base 23465d5.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Jul 31, 2026
… rows
CodeRabbit found row #165 claiming its follow-ups "were renumbered again to
#156/#157/#158" — ids that now belong to unrelated rows on main. A reference
that resolves to the wrong row is worse than none in a ledger whose whole
job is to be followable later.
Two fixes:
- Dropped the renumbering trail entirely rather than updating it. It listed
superseded ids that main has since reissued, and it went stale again on
the very next sync; the row now states that renumbering happened and why,
without naming numbers that no longer mean anything.
- Corrected a second stale reference the earlier renumber missed: row #165
pointed at #157 for the privacy sign-off that variant F would need. That
is the answer-mode caveat row, now #166. The earlier pass only rewrote
three known cross-reference phrasings and did not catch this one.
Verified by extracting every backtick id reference from the three rows: #165
and #166 now point at each other and #167 at #120, all live.
check:outstanding-issues: 165 rows (50 open, 115 archived), unique ids,
next-id=168 above the highest, no ids deleted from base 3b4872f.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPyFcMfn1jMmphr6AqiWBg
BigSimmo pushed a commit that referenced this pull request Aug 12, 2026
…re-scope the wave plan
Archived with source evidence:
- #166 the verify-before-use caveat ships (verification-notice.tsx:52, rendered
unconditionally by AnswerCard at answer-card.tsx:108). I reported this row as
open twice before reading the code; the archive says so.
- #208 buildAnswerClipboardText composes renderCopyText rather than replacing
formatAnswerRenderCopyText (answer-copy-payload.ts:121-138).
- #216 AnswerCard is adopted on the answer surface and the manifest agrees.
Re-scoped #250: its wave plan names A1 rows that are now closed and hygiene
rows that are in flight, so as written it misdirects. Recorded what actually
remains (#147, #117, #118, #98, #189).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017paT42ZVMf8jaLtkjFxdy5
Sign up for freeto 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.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Preserve client-error status for malformed upload bodies**

2 participants

@BigSimmo