Skip to content

feat(jobs): render structured job-error bodies on submit and /my-jobs#218

Merged
samseaver merged 1 commit into
ModelSEED:stagingfrom
VibhavSetlur:staging
Jun 10, 2026
Merged

feat(jobs): render structured job-error bodies on submit and /my-jobs#218
samseaver merged 1 commit into
ModelSEED:stagingfrom
VibhavSetlur:staging

Conversation

@VibhavSetlur

Copy link
Copy Markdown
Collaborator

Summary

Implements both rendering paths from modelseed-api/docs/JOB_ERROR_UI_INTEGRATION.md so users see actionable failures instead of a bare "Failed" badge or a modelseed-api ... failed (404) stub.

Path 1 — sync 4xx on submit (new backend behavior, live 2026-06-10)

  • lib/api/modelseed.ts introduces ModelseedApiError carrying { status, detail } and extractApiErrorDetail(). modelseedFetch now throws the typed error instead of a flat Error, preserving the structured { code, message, hint, field, retryable } body.
  • lib/utils/jobErrors.ts adds presentJobSubmitError() which lifts structured fields onto a render-ready shape and flags TOKEN_EXPIRED (also defensively flags any bare 401).
  • components/ui/JobSubmitErrorAlert.tsx (new) renders the message as the Alert title, hint in lighter weight beneath, and an Affected input: <field> caption.
  • lib/hooks/useTokenExpiredRedirect.ts (new) clears auth and redirects to /?returnTo=…&reason=token_expired. The home page shows a one-time "Session expired" notice above the sign-in form when the reason is present.
  • Three submit call sites are wired through this flow: reconstruct (app/(build-model)/plant/page.tsx), FBA/Gapfill (app/model/[...path]/page.tsx via a new actionError prop on ModelDetailHeader), and merge (app/(user-data)/my-models/page.tsx).
  • Unknown code values still render message + hint (open enum, no hard failure).

Path 2 — async job.error on /my-jobs (pre-existing backend behavior)

  • The Failed chip itself is now clickable with a tooltip preview of the error string. The existing info-icon action button still works as an alternate entry point.
  • New components/ui/FailedJobDetailsDialog.tsx renders the full job.error (scrollable, monospace), the last job.progress note, the parameters.arguments block as pretty JSON, and a deep link to the output_path model when one is recorded.
  • mergeApiAndTrackedJobs now plumbs progress and parameters onto the row so the dialog has the data without a second fetch.

Test coverage

  • 11 new unit tests: ModelseedApiError parsing from 404/422/401/empty bodies; presentJobSubmitError for structured, unstructured, legacy _ERROR_Object not found_ERROR_, and TOKEN_EXPIRED cases.
  • 6 new Playwright e2e tests: sync 4xx alert renders GENOME_NOT_FOUND with message + hint + field, Failed chip tooltip, Failed chip click → dialog, info-icon click → dialog, session-expired notice shown/hidden based on ?reason=token_expired. Backend calls are stubbed via page.route, so no live PATRIC token is required.
  • playwright.config.ts now respects PLAYWRIGHT_PORT / PLAYWRIGHT_BASE_URL so tests can run against a dev server bound to a non-default port.

All 108 unit tests pass; the 6 new e2e tests pass on a local dev server. The other pre-existing e2e failures are unrelated (they need a real PATRIC_TOKEN to populate the model grid) and reproduce on staging without these changes.

Test plan

  • npm run test:run — 108/108 pass
  • npx tsc --noEmit — clean
  • npx eslint --quiet . — clean
  • PLAYWRIGHT_PORT=3005 PLAYWRIGHT_BASE_URL=http://localhost:3005 npx playwright test tests/e2e/jobs/job-error-integration.spec.ts — 6/6 pass
  • Once merged to staging, exercise on the live deploy with the doc's curl recipes (bad genome → see GENOME_NOT_FOUND alert; expired token → see session-expired notice on bounce-back)

🤖 Generated with Claude Code

Implements both rendering paths described in the modelseed-api repo's
docs/JOB_ERROR_UI_INTEGRATION.md so users see actionable failures instead
of a bare "Failed" badge or a "modelseed-api ... failed (404)" stub.

Path 1 (sync 4xx on submit): modelseedFetch now throws ModelseedApiError
carrying { status, detail } parsed from FastAPI's `{ detail: {...} }`
body. The reconstruct, FBA, gapfill, and merge submit handlers route
errors through presentJobSubmitError() and a new JobSubmitErrorAlert
that renders message + hint + affected-input. TOKEN_EXPIRED triggers
useTokenExpiredRedirect() to clear auth and bounce to the home page
with `?reason=token_expired` so a one-time "Session expired" notice
greets the user on the sign-in form.

Path 2 (async job.error on /my-jobs): the Failed chip is now clickable
with a tooltip preview, and a new FailedJobDetailsDialog shows the full
error string, the last progress note, parameters, and a deep link to
the targeted model. The info-icon action button opens the same dialog.

Tests: 11 new unit tests cover ModelseedApiError parsing and the
presentJobSubmitError helper; 6 new Playwright e2e tests cover the
sync-4xx alert, the failed-job dialog (chip + icon entry points), and
the session-expired notice. playwright.config.ts now reads
PLAYWRIGHT_PORT / PLAYWRIGHT_BASE_URL so tests can target a dev server
on a non-default port.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@samseaver samseaver merged commit ad861a8 into ModelSEED:staging Jun 10, 2026
2 checks passed
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