Skip to content

fix(backend): return 404 instead of 500 for audio of failed generations#893

Open
flowbrix wants to merge 3 commits into
jamiepine:mainfrom
flowbrix:claude/blissful-liskov-3831c0
Open

fix(backend): return 404 instead of 500 for audio of failed generations#893
flowbrix wants to merge 3 commits into
jamiepine:mainfrom
flowbrix:claude/blissful-liskov-3831c0

Conversation

@flowbrix

@flowbrix flowbrix commented Jul 13, 2026

Copy link
Copy Markdown

What

GET /audio/{generation_id} returned a 500 (RuntimeError: File at path .../data is not a file) for generations with status failed, because a failed generation stores an empty audio_path. It now returns a clean 404 with "Generation failed; no audio available".

Why it happened

The route already had a 404 guard, but it was defeated upstream: config.resolve_storage_path("") resolved the empty string to (_data_dir / "").resolve() — the data directory itself. A directory passes .exists(), so the guard let it through and FileResponse blew up on a non-file path.

Changes

  • config.resolve_storage_path now returns None for empty paths instead of resolving to the data dir (all ~25 call sites already handle None; this also protects samples/avatars/captures endpoints from the same trap).
  • The three audio routes check is_file() instead of exists(), so a directory can never reach FileResponse.
  • GET /audio/{generation_id} distinguishes the failed-generation case in its 404 detail ("Generation failed; no audio available" vs "Audio file not found").

Tests

Added backend/tests/test_audio_failed_generation.py covering: empty audio_path, NULL audio_path, completed generation with a missing file, and unknown generation id — all return 404 with the expected detail, plus a unit test on resolve_storage_path(""). Verified the tests fail against the pre-fix code.

Repro (before this fix): create a generation that fails (e.g. kill the engine mid-generation), then GET /audio/{id} → 500.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Audio endpoints now verify resolved audio paths as actual files (not directories) before serving.
    • Failed generations return HTTP 404 with “Generation failed; no audio available”; missing audio for completed generations returns “Audio file not found”.
    • Empty or invalid stored audio paths no longer bypass “not found” handling and now respond with the correct 404 details.
  • Tests
    • Added regression coverage for empty/invalid stored paths, failed generation audio behavior, missing audio files (including directory cases), and unknown generation IDs.

A failed generation stores an empty audio_path. resolve_storage_path("")
resolved to the data directory itself, which exists, so the route's 404
guard passed and FileResponse raised RuntimeError ("File at path .../data
is not a file"), surfacing as a 500.

- resolve_storage_path now returns None for empty paths
- audio routes check is_file() instead of exists() so directories never
  reach FileResponse
- GET /audio/{generation_id} reports "Generation failed; no audio
  available" when the generation status is failed

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5355b8d7-3d60-42bc-93d7-6a055ab2e7cd

📥 Commits

Reviewing files that changed from the base of the PR and between 761b8d1 and b44f2c9.

📒 Files selected for processing (1)
  • backend/tests/test_audio_failed_generation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/tests/test_audio_failed_generation.py

📝 Walkthrough

Walkthrough

Storage path resolution now rejects empty values, audio endpoints require regular files, and generation audio returns status-specific 404 details. Regression tests cover failed, missing-file, directory, and unknown-generation cases.

Changes

Audio path handling

Layer / File(s) Summary
Storage path resolution contract
backend/config.py, backend/tests/test_audio_failed_generation.py
Empty stored paths, including "", None, and Path(""), return None and are covered by direct assertions.
Audio endpoint validation and responses
backend/routes/audio.py, backend/tests/test_audio_failed_generation.py
Audio routes require regular files and return distinct 404 details for failed generations, missing files, directories, and unknown generations; tests construct isolated FastAPI and SQLite fixtures for these cases.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: returning 404 for unavailable audio on failed generations.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
backend/routes/audio.py (1)

37-38: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add directory-path regression coverage for version and sample audio.

is_file() matters when the resolved path exists but is a directory. Please add or verify tests for version and sample records pointing to existing directories and assert 404, preventing a regression to exists() that could let FileResponse return a 500.

Also applies to: 80-81

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/routes/audio.py` around lines 37 - 38, Extend the tests covering
version and sample audio endpoints to use records whose resolved audio paths
point to existing directories, and assert both return HTTP 404. Keep the
implementation checks in the audio route’s audio_path handling and ensure
coverage prevents replacing is_file() with exists().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/config.py`:
- Around line 77-84: Update resolve_storage_path to handle None separately and
return None when the supplied Path has no parts, preventing Path() from
resolving to the configured data directory. Preserve the existing empty-string
behavior, and add a regression test covering Path() alongside that case.

---

Nitpick comments:
In `@backend/routes/audio.py`:
- Around line 37-38: Extend the tests covering version and sample audio
endpoints to use records whose resolved audio paths point to existing
directories, and assert both return HTTP 404. Keep the implementation checks in
the audio route’s audio_path handling and ensure coverage prevents replacing
is_file() with exists().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d5dad44f-aaaa-4f93-a9b6-88eb0f8b1205

📥 Commits

Reviewing files that changed from the base of the PR and between f2cf2a7 and 067f827.

📒 Files selected for processing (3)
  • backend/config.py
  • backend/routes/audio.py
  • backend/tests/test_audio_failed_generation.py

Comment thread backend/config.py
Path("") is truthy, so the previous `if not path` guard only caught
None and empty strings. Callers such as database/migrations.py pass
Path objects, so an empty Path could still resolve to the data dir.
Check None separately and reject paths with no parts.

Also add regression tests asserting the version and sample audio
endpoints 404 when a stored path resolves to an existing directory
(guards the is_file() checks against regressing to exists()).

Addresses CodeRabbit review on PR jamiepine#893.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
backend/tests/test_audio_failed_generation.py (1)

47-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use @pytest.fixture without parentheses.

Ruff PT001 flags Line 47; change @pytest.fixture() to @pytest.fixture.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_audio_failed_generation.py` at line 47, Replace the
parenthesized decorator on the affected pytest fixture with the bare
`@pytest.fixture` form, without changing the fixture’s implementation or behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@backend/tests/test_audio_failed_generation.py`:
- Line 47: Replace the parenthesized decorator on the affected pytest fixture
with the bare `@pytest.fixture` form, without changing the fixture’s
implementation or behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 82f39ef3-1fe7-46b2-a98a-f019261c6416

📥 Commits

Reviewing files that changed from the base of the PR and between 067f827 and 761b8d1.

📒 Files selected for processing (2)
  • backend/config.py
  • backend/tests/test_audio_failed_generation.py

Co-Authored-By: Claude Fable 5 <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.

1 participant