Skip to content

fix(media): scale commit timeout with size; surface structured API errors - #4

Merged
mrgoonie merged 1 commit into
mainfrom
claude/brave-cohen-239b4a
Aug 6, 2026
Merged

fix(media): scale commit timeout with size; surface structured API errors#4
mrgoonie merged 1 commit into
mainfrom
claude/brave-cohen-239b4a

Conversation

@mrgoonie

@mrgoonie mrgoonie commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Found during a live-backend smoke test against staging (api.ab-stg.agentbrain.sh, real Cloudflare R2). Two fixes + one robustness improvement, all verified live.

1. Commit timeout too short for large media (HIGH)

  • putAbsoluteBytes (the PUT) already scales its abort window with size, but the commit POST used the fixed 30s request() timeout.
  • Backend commit for media kinds streams the whole object from R2 through ffprobe; measured ~52s for a 300MB object → CLI aborted at 30s with a spurious 408, leaving the asset stuck at status:"uploaded".
  • Fix: optional per-request timeoutMs; uploadMedia passes commitTimeoutMs(sizeBytes) (floor 60s, +1s/MB → 300s @300mb).
  • Live re-verify: commit now runs the full ~52s and returns the real backend result — no premature 408.

2. API errors rendered [object Object] (MEDIUM)

  • Backend envelope is { success:false, error:{ id, code, status, message }, message }. data.error ?? data.message stringified the object[object Object] for every structured 4xx (413/422/…).
  • Fix: extractApiErrorMessage() prefers error.message, then top-level message, then string body, then statusText (preserves the existing string-error case).
  • Live re-verify: 422 now shows media: content stream probe mismatch: … ffprobe failed ….

3. 409-as-success reuses echoed payload

  • The backend's 409 (already-committed) body includes data:{assetId,status,objectKey}; reuse it instead of discarding objectKey.

Tests

  • +6 unit tests (28 total pass), pnpm lint clean, pnpm build OK.

Follow-ups (not in this PR)

  • Flat MAX_UPLOAD_BYTES=500MB ignores per-kind backend caps (measured: raw_doc <100MB, image <300MB, video ~500MB) → client could pre-validate.
  • Unknown/extensionless files default to application/octet-streamraw_doc, which the backend rejects (invalid_mime_for_kind) → require --content-type for unknown extensions.

Smoke-test coverage (live)

me get ✅ · small media upload ✅ (presign→PUT R2→commit ready) · knowledge list ✅ · tag create ✅ · 300MB upload PUT ✅ + commit path root-caused/fixed.

… errors

Large media commits run backend-side ffprobe that streams the whole object
from storage (~50s for 300MB), exceeding the fixed 30s request timeout and
aborting a succeeding commit with a spurious 408 (asset left in 'uploaded').
Add an optional per-request timeoutMs and pass a size-scaled window to commit.

Backend error envelopes wrap the message in { error: { message }, message },
so 'data.error ?? data.message' stringified the object as [object Object] for
every structured 4xx. Extract error.message / top-level message instead.

Also reuse the asset payload the backend echoes in a 409 (already-committed)
body instead of discarding objectKey.
@mrgoonie
mrgoonie merged commit ad0506f into main Aug 6, 2026
@mrgoonie
mrgoonie deleted the claude/brave-cohen-239b4a branch August 6, 2026 05:49
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.

1 participant