Skip to content

fix(ai-fal): handle errors from fal result fetch on completed jobs#396

Open
tombeckenham wants to merge 3 commits intoTanStack:mainfrom
tombeckenham:394-fal-video-adapter-failed-queue-status-mapped-to-processing-causes-infinite-polling
Open

fix(ai-fal): handle errors from fal result fetch on completed jobs#396
tombeckenham wants to merge 3 commits intoTanStack:mainfrom
tombeckenham:394-fal-video-adapter-failed-queue-status-mapped-to-processing-causes-infinite-polling

Conversation

@tombeckenham
Copy link
Contributor

@tombeckenham tombeckenham commented Mar 23, 2026

Summary

  • fal.ai never returns a FAILED queue status — invalid jobs go IN_PROGRESSCOMPLETED, and the real error (e.g. 422 validation) only surfaces when calling fal.queue.result()
  • getVideoUrl() now catches result fetch errors and extracts detailed validation messages from error.body.detail
  • Removed fictional FAILED queue status handling (fal doesn't use it)
  • getVideoJobStatus() now returns status: 'failed' when the result fetch throws on a "completed" job
  • Unknown queue statuses default to 'processing' (safe for polling) instead of 'failed'

Closes #394

Test plan

  • Verified behavior against live fal.ai API with a prompt exceeding 2500 char limit
  • Added test for 422 validation error extraction in getVideoUrl()
  • Updated unknown status test to expect 'processing'
  • All 25 tests pass (15 video + 10 image)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Detect and surface fal.ai result-fetch failures so completed-but-broken jobs are reported as failed instead of remaining non-failed.
    • Extract and show detailed validation/error messages from fal.ai responses when available.
  • Chores
    • Bumped fal.ai client dependency to the latest patch release.
  • Tests
    • Added tests verifying status mapping and detailed error messages on result-fetch failures.

Map fal.ai's FAILED queue status to 'failed' instead of falling through
to the default case which incorrectly returned 'processing'. Unknown
statuses now also map to 'failed' as a safety net. Error details from
fal responses are surfaced in VideoStatusResult.error.

Closes TanStack#394

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tombeckenham tombeckenham requested a review from a team March 23, 2026 09:54
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1028bed7-4e6b-4bf2-ab5b-2ebdb9a9e18b

📥 Commits

Reviewing files that changed from the base of the PR and between 00f4442 and 20ff087.

📒 Files selected for processing (1)
  • .changeset/fix-fal-failed-status.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/fix-fal-failed-status.md

📝 Walkthrough

Walkthrough

Updates fal.ai video adapter error handling and status mapping: bumps @fal-ai/client, maps FAILED and unknown statuses to 'failed', surfaces validation/error details from result fetches, adjusts activity to mark completed-but-unfetchable jobs as failed, and adds tests and a changeset.

Changes

Cohort / File(s) Summary
Changeset & Package
\.changeset/fix-fal-failed-status.md, packages/typescript/ai-fal/package.json
Adds a patch changeset and updates @fal-ai/client from ^1.9.1 to ^1.9.4.
Adapter: types & status mapping
packages/typescript/ai-fal/src/adapters/video.ts
Maps fal FAILED (and unknown) to 'failed', updates docs/comments, surfaces fal error details on result fetch, and wraps queue.result(...) with improved error parsing and throwing.
Activity logic
packages/typescript/ai/src/activities/generateVideo/index.ts
When fetching URL for a COMPLETED provider report throws, emits video:request:completed with status: 'failed' and returns status: 'failed' with a normalized error message.
Tests
packages/typescript/ai-fal/tests/video-adapter.test.ts
Adds tests for unrecognized queue status mapping and for structured validation errors from fal that produce detailed thrown messages.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Adapter
  participant FalClient
  participant Activity as VideoActivity

  Client->>Adapter: createVideoJob / poll getVideoStatus(jobId)
  Adapter->>FalClient: queue.status(jobId)
  FalClient-->>Adapter: { status: "COMPLETED" | "FAILED" | ... , error? }
  alt status == COMPLETED
    Adapter->>FalClient: queue.result(jobId)
    FalClient--xAdapter: throws validation/error (body.detail)
    Adapter->>Activity: emit video:request:completed (status: 'failed', error)
    Adapter-->>Client: return { status: 'failed', error, progress }
  else status == FAILED
    Adapter->>Activity: emit video:request:completed (status: 'failed', error)
    Adapter-->>Client: return { status: 'failed', error }
  else other/unknown
    Adapter-->>Client: return { status: 'failed' }
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through queues that looped and twirled,
Found FAILED hiding where completions hurled,
I caught the details, stitched status right,
Now polling ends and errors light.
A cheerful hop — the pipeline's bright! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely and accurately describes the main fix: handling errors from fal result fetches on completed jobs.
Description check ✅ Passed The description covers the key changes and includes test results, but lacks explicit checklist marks for required items.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #394: error extraction from fal.ai responses, status mapping for failed jobs, and unknown status handling.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the fal video adapter error handling issue; no out-of-scope modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Mar 23, 2026

View your CI Pipeline Execution ↗ for commit 20ff087

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 3m 52s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-23 10:34:34 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 23, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@396

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@396

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@396

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@396

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@396

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@396

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@396

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@396

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@396

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@396

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@396

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@396

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@396

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@396

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@396

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@396

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@396

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@396

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@396

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@396

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@396

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@396

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@396

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@396

commit: 20ff087

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/typescript/ai-fal/tests/video-adapter.test.ts (1)

217-227: Good safety net test for unknown statuses.

This test ensures that any unrecognized status from fal.ai will result in 'failed' rather than continuing to poll indefinitely.

Consider adding an edge case test for FAILED without an error message to ensure graceful handling:

📝 Optional additional test case
it('returns failed status without error when error field is absent', async () => {
  mockQueueStatus.mockResolvedValueOnce({
    status: 'FAILED',
  })

  const adapter = createAdapter()

  const result = await adapter.getVideoStatus('job-failed-no-error')

  expect(result.status).toBe('failed')
  expect(result.error).toBeUndefined()
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-fal/tests/video-adapter.test.ts` around lines 217 -
227, Add a test that ensures the adapter maps a fal.ai response with status
'FAILED' but no error field to { status: 'failed' } and does not set an error
message: create a new it block similar to the existing unknown status test, use
mockQueueStatus.mockResolvedValueOnce({ status: 'FAILED' }), call
createAdapter() and await adapter.getVideoStatus('job-failed-no-error'), then
assert result.status === 'failed' and that result.error is undefined; reference
mockQueueStatus, createAdapter, and getVideoStatus to locate where to add the
test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/typescript/ai-fal/src/adapters/video.ts`:
- Around line 19-26: Update the FalQueueStatus type to only include 'IN_QUEUE' |
'IN_PROGRESS' | 'COMPLETED' (remove 'FAILED') and ensure any failure detection
logic uses FalStatusResponse.error (i.e., check for status === 'COMPLETED' &&
error) rather than comparing to a non-existent 'FAILED' status; update the
FalStatusResponse type usage sites accordingly so callers handle
completed-with-error cases.

---

Nitpick comments:
In `@packages/typescript/ai-fal/tests/video-adapter.test.ts`:
- Around line 217-227: Add a test that ensures the adapter maps a fal.ai
response with status 'FAILED' but no error field to { status: 'failed' } and
does not set an error message: create a new it block similar to the existing
unknown status test, use mockQueueStatus.mockResolvedValueOnce({ status:
'FAILED' }), call createAdapter() and await
adapter.getVideoStatus('job-failed-no-error'), then assert result.status ===
'failed' and that result.error is undefined; reference mockQueueStatus,
createAdapter, and getVideoStatus to locate where to add the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 61ab5164-14e9-44be-8b98-008f8aa8a168

📥 Commits

Reviewing files that changed from the base of the PR and between c3583e3 and 9257fc4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • .changeset/fix-fal-failed-status.md
  • packages/typescript/ai-fal/package.json
  • packages/typescript/ai-fal/src/adapters/video.ts
  • packages/typescript/ai-fal/tests/video-adapter.test.ts

fal.ai never returns a FAILED queue status — invalid jobs go through
IN_PROGRESS → COMPLETED, and the real error (e.g. 422 validation)
only surfaces when calling fal.queue.result(). This extracts detailed
error info from the result fetch, removes the fictional FAILED status
handling, and returns failed status from getVideoJobStatus when the
result fetch throws.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tombeckenham tombeckenham changed the title fix(ai-fal): handle FAILED queue status to prevent infinite polling fix(ai-fal): handle errors from fal result fetch on completed jobs Mar 23, 2026
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

fal video adapter: FAILED queue status mapped to 'processing', causes infinite polling

2 participants