Skip to content

fix: return 400 for unresolvable model selectors - #254

Merged
njbrake merged 4 commits into
mozilla-ai:mainfrom
AloysJehwin:fix/bad-model-name-returns-500
Jul 20, 2026
Merged

fix: return 400 for unresolvable model selectors#254
njbrake merged 4 commits into
mozilla-ai:mainfrom
AloysJehwin:fix/bad-model-name-returns-500

Conversation

@AloysJehwin

@AloysJehwin AloysJehwin commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Description

resolve_provider_selector raises ValueError for a selector with no provider: prefix and AnyLLMError for an unknown provider. Every dispatch site called it without catching either — a typo like nosuchmodel or nobody:model escaped as a bare 500 Internal Server Error. Both are client input errors and should return 400 with a useful detail string.

PR Type

  • New Feature
  • Bug Fix
  • Refactor
  • Documentation
  • Infrastructure / CI

Relevant issues

Closes #252

Changes

src/gateway/api/routes/_pipeline.py

  • Add _raise_for_unresolvable_model(model_selector, exc) helper — single place that maps ValueError/AnyLLMErrorHTTP 400 with the bad model name in the detail
  • Wrap the fallback resolve_provider_selector call in resolve_dispatch_provider with try/except_raise_for_unresolvable_model (covers chat, messages, responses routes)

src/gateway/api/routes/{embeddings,images,audio,rerank,moderations,batches}.py

  • Import _raise_for_unresolvable_model and AnyLLMError
  • Wrap each bare resolve_provider_selector call with the same guard

tests/unit/test_bad_model_name_returns_400.py — 7 new unit tests

Checklist

  • I understand the code I am submitting.
  • I have added or updated tests that cover my change (tests/unit, tests/integration).
  • I ran the Definition of Done checks locally (make lint, make typecheck, make test).
  • Documentation was updated where necessary.
  • If the API contract changed, I regenerated the OpenAPI spec (uv run python scripts/generate_openapi.py).

AI Usage

  • No AI was used.
  • AI was used for drafting/refactoring.
  • This is fully AI-generated.

AI Model/Tool used: Claude (claude-sonnet)

Any additional AI details you'd like to share: Used to identify the bug, trace the call path, write the fix and unit tests.

  • I am an AI Agent filling out this form (check box if true)

Summary

  • Model/selector resolution failures that previously surfaced as HTTP 500 now return HTTP 400 with the invalid model name included in the response detail.
  • Standard and pass-through routes were updated to apply the same client-friendly error mapping across chat, messages, responses, embeddings, images, audio, rerank, moderations, and batches.
  • Added centralized handling for “unparseable/unknown” model selectors, including preserving reserved budget behavior when resolution fails in reservation-first flows.
  • Expanded test coverage:
    • New unit tests for the selector/provider resolution error-to-HTTP-400 mapping.
    • Updated integration assertions to require the invalid model string in the error detail.
    • Added a regression test for billable pass-through routes to fail fast with HTTP 400 when the model selector can’t be resolved.

Technical notes

  • Introduced an internal _raise_for_unresolvable_model helper to convert ValueError and AnyLLMError into a standardized HTTPException (400) that instructs callers to use provider:model.
  • Updated resolution and pass-through routing code paths to use this helper, including refunding reserved budget when applicable.

…not 500 (fixes mozilla-ai#252)

resolve_provider_selector raises ValueError for selectors with no
provider: prefix and AnyLLMError for unknown providers. Every dispatch
site called it without catching either, so a typo like "nosuchmodel"
or "nobody:model" escaped as a bare 500 Internal Server Error.

Fix: add _raise_for_unresolvable_model in _pipeline.py that maps both
exceptions to HTTP 400 with a detail string naming the bad selector.
Wrap the bare call in resolve_dispatch_provider (chat, messages,
responses routes) and each bare resolve_provider_selector call site
(embeddings, images, audio, rerank, moderations, batches).

Add 7 unit tests covering ValueError/AnyLLMError mapping, model name
in detail, cached-provider fast-path, fresh-resolution success, and
both error paths through resolve_dispatch_provider.

Signed-off-by: Aloys Jehwin <aloys.jehwin@sap.com>
@github-actions github-actions Bot added the missing-template PR is missing required template sections label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a76f4049-cd0c-49cc-8994-394ad968594d

📥 Commits

Reviewing files that changed from the base of the PR and between ae9dcde and 2f37b5c.

📒 Files selected for processing (2)
  • src/gateway/api/routes/_passthrough.py
  • src/gateway/api/routes/_pipeline.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/gateway/api/routes/_passthrough.py
  • src/gateway/api/routes/_pipeline.py

Walkthrough

Changes

Model resolution errors

Layer / File(s) Summary
Shared resolution error mapping
src/gateway/api/routes/_pipeline.py
Adds a centralized HTTP 400 response for unparseable selectors and unknown providers, and applies it during dispatch provider resolution.
Route resolution handling
src/gateway/api/routes/batches.py, src/gateway/api/routes/_passthrough.py
Batch and pass-through routes catch both resolution failures; reservation-first pass-through requests refund reservations before raising the standardized error.
Resolution error tests
tests/unit/test_bad_model_name_returns_400.py, tests/integration/test_alias_api.py, tests/integration/test_batches_endpoint.py, tests/integration/test_passthrough_enforcement.py
Tests verify HTTP 400 details, cached and resolved providers, deleted aliases, batch validation, and unresolvable models across billable routes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: tbille

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the required fix: prefix and clearly describes the main change.
Description check ✅ Passed The description matches the template well and includes the required sections, issue link, checklist, and AI usage details.
Linked Issues check ✅ Passed The PR addresses #252 by catching unparseable and unknown-provider selectors and returning HTTP 400 across the affected routes.
Out of Scope Changes check ✅ Passed The code and tests stay focused on model-selector error mapping; no unrelated changes stand out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

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

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)
src/gateway/api/routes/batches.py (1)

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

Remove redundant try...except block.

Hey there! Since the new inner block catches ValueError (along with AnyLLMError) and delegates it to our helper which raises an HTTPException, the outer except ValueError block below this is actually unreachable now. We can safely remove the extra nesting to keep the code nice and clean!

♻️ Proposed refactor
-    try:
-        try:
-            resolved = resolve_provider_selector(config, request.model)
-        except (ValueError, AnyLLMError) as exc:
-            _raise_for_unresolvable_model(request.model, exc)
-    except ValueError as e:
-        raise HTTPException(
-            status_code=status.HTTP_400_BAD_REQUEST,
-            detail=f"Invalid request: {e}",
-        ) from e
+    try:
+        resolved = resolve_provider_selector(config, request.model)
+    except (ValueError, AnyLLMError) as exc:
+        _raise_for_unresolvable_model(request.model, exc)
🤖 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 `@src/gateway/api/routes/batches.py` around lines 126 - 129, Remove the
redundant try/except around resolve_provider_selector in the batch route. Keep
the existing ValueError and AnyLLMError handling delegated through
_raise_for_unresolvable_model, and eliminate the now-unreachable outer
ValueError handling while preserving the route’s current HTTPException behavior.
🤖 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 `@src/gateway/api/routes/audio.py`:
- Around line 105-108: Move the provider resolution block and all dependent
assignments through provider_kwargs before reserve_budget in both
src/gateway/api/routes/audio.py lines 105-108 and 270-273. Update the
corresponding audio route flows around resolve_provider_selector so unresolvable
models raise before budget reservation, while preserving the existing
reservation and refund behavior for resolvable models.

---

Nitpick comments:
In `@src/gateway/api/routes/batches.py`:
- Around line 126-129: Remove the redundant try/except around
resolve_provider_selector in the batch route. Keep the existing ValueError and
AnyLLMError handling delegated through _raise_for_unresolvable_model, and
eliminate the now-unreachable outer ValueError handling while preserving the
route’s current HTTPException behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1d376532-5d22-456f-a7fc-889ce0e21717

📥 Commits

Reviewing files that changed from the base of the PR and between ed1a531 and 448f3ad.

📒 Files selected for processing (8)
  • src/gateway/api/routes/_pipeline.py
  • src/gateway/api/routes/audio.py
  • src/gateway/api/routes/batches.py
  • src/gateway/api/routes/embeddings.py
  • src/gateway/api/routes/images.py
  • src/gateway/api/routes/moderations.py
  • src/gateway/api/routes/rerank.py
  • tests/unit/test_bad_model_name_returns_400.py

Comment thread src/gateway/api/routes/audio.py Outdated
Comment on lines +105 to +108
try:
resolved = resolve_provider_selector(config, model)
except (ValueError, AnyLLMError) as exc:
_raise_for_unresolvable_model(model, exc)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Fix budget reservation leak on unresolvable models.

Great work standardizing these model resolution errors! I just spotted a sneaky little budget leak in the audio routes.

Since resolve_provider_selector is called after reserve_budget, an unresolvable model will cause _raise_for_unresolvable_model to raise an HTTPException that escapes before reaching our refund block. This leaves the budget reserved indefinitely and violates our guideline to refund on every error path.

  • src/gateway/api/routes/audio.py#L105-L108: Let's move this provider resolution block (and the subsequent assignments up to provider_kwargs) above the reserve_budget call to prevent the leak, just like you did in the other routes.
  • src/gateway/api/routes/audio.py#L270-L273: Same here! Move this resolution block above the reserve_budget call.
📍 Affects 1 file
  • src/gateway/api/routes/audio.py#L105-L108 (this comment)
  • src/gateway/api/routes/audio.py#L270-L273
🤖 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 `@src/gateway/api/routes/audio.py` around lines 105 - 108, Move the provider
resolution block and all dependent assignments through provider_kwargs before
reserve_budget in both src/gateway/api/routes/audio.py lines 105-108 and
270-273. Update the corresponding audio route flows around
resolve_provider_selector so unresolvable models raise before budget
reservation, while preserving the existing reservation and refund behavior for
resolvable models.

Source: Coding guidelines

@github-actions github-actions Bot removed the missing-template PR is missing required template sections label Jul 15, 2026

@njbrake njbrake left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note: this review was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.

Thanks for this. The diagnosis is right and the fix is in the right place: I read resolve_provider_selector and confirmed it raises ValueError for an unparseable selector and AnyLLMError for an unknown provider, so catching both and mapping to 400 is correct. Centralizing that in _raise_for_unresolvable_model is a good call, and there is no circular import (all six route modules import cleanly).

The blocker is that the PR does not pass the Definition of Done checks the checklist marks as run. Both make lint and make typecheck fail:

make lint: 7 I001 import-sort errors. The new imports were prepended out of order in every modified source file. For example from any_llm.exceptions import AnyLLMError lands before from any_llm import ..., and the _pipeline import drops into the middle of the gateway.services block. ruff check --fix resolves all 7.

make typecheck: 11 mypy errors. The decisive one is in _pipeline.py: resolve_dispatch_provider reports Missing return statement. Because _raise_for_unresolvable_model is typed -> None, mypy believes the except branch can fall through without returning a ResolvedProvider. Typing the helper -> NoReturn (it always raises) fixes that and documents intent. The other 10 errors are the two untyped test helpers (_make_ctx, _make_config) and their call sites under mypy strict.

Both checks are enforced in CI (otari-lint.yml, otari-typecheck.yml), so this is red as submitted.

Two smaller items:

  • batches.py has dead code. The inner catch delegates to the helper, which raises HTTPException, not ValueError, so the outer except ValueError is now unreachable. It can be dropped, collapsing the double try into one. CodeRabbit flagged the same thing.
  • audio.py reserves budget before resolving the model. CodeRabbit noted this; it is worth mentioning but not blocking. The reservation amount is 0.0 and the ordering predates this PR, so no behavior regresses here. If you want resolution to gate reservation, that is a separate, small follow-up.

One suggestion, not blocking: the new tests exercise the helper and resolve_dispatch_provider in isolation but nothing asserts a route returns 400 end to end. An integration case (for example POST /v1/embeddings with model="nosuchmodel") would lock in the wiring across all six routes.

I have the lint fix, the NoReturn fix, the test annotations, and the batches.py dead-code removal staged locally; with those applied, ruff is clean, mypy is clean across 195 files, and the 7 new tests pass. Happy to push them to this branch if that is easier than reworking it yourself.

- _raise_for_unresolvable_model typed -> NoReturn so mypy sees it always raises
- ruff I001 import-sort fixes across all 7 modified route files + test file
- remove unreachable outer except ValueError in batches.py (dead code after inner handler delegates to helper)
- type annotate _make_ctx / _make_config test helpers to satisfy mypy strict

Signed-off-by: Aloys Jehwin <aloysjehwin@gmail.com>
@AloysJehwin

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review @njbrake — addressed all three blockers in the latest commit:

  • -> NoReturn on _raise_for_unresolvable_model — mypy now correctly infers the except branch always raises, so resolve_dispatch_provider has no missing-return error
  • Import sort — ran ruff check --fix across all 7 modified source files + the test file; all I001 errors cleared
  • Dead except ValueError in batches.py — collapsed the double try to a single block as suggested (CodeRabbit flagged the same)
  • Test helper annotations — added -> MagicMock return types and typed the resolved_provider param; mypy strict clean across all 195 files

make lint and make typecheck both pass cleanly now.

The branch predated mozilla-ai#275, which consolidated the pass-through routes
(audio, embeddings, images, moderations, rerank) onto the shared
run_passthrough helper and moved provider resolution out of the
individual route files. The branch's per-route edits therefore no longer
applied. Resolve the merge by dropping those obsolete edits and moving
the fix to where resolution now happens:

- _pipeline.py: keep the _raise_for_unresolvable_model helper and the
  guarded resolve_dispatch_provider (covers chat, messages, responses).
- _passthrough.py: guard both resolve_provider_selector calls in
  run_passthrough. On the reserve-before-resolve path (audio) refund the
  held reservation before raising the 400, closing the budget leak
  CodeRabbit flagged.
- batches.py: unify on _raise_for_unresolvable_model so an unknown
  provider (AnyLLMError), not just an unparseable selector (ValueError),
  maps to 400.

Tests:
- test_passthrough_enforcement.py: assert every billable route (including
  the audio reserve-first routes) returns 400, not 500, for an
  unresolvable model and never reaches the provider.
- test_alias_api.py: a deleted alias now surfaces as a 400 response
  rather than a bare ValueError; keep the no-dispatch guarantee.
- test_batches_endpoint.py: assert the model name appears in the detail.

make lint, make typecheck, generate_openapi --check, and the full unit +
integration suites pass (only two pre-existing, network-dependent
provider-error tests fail identically on origin/main in this sandbox).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@njbrake njbrake changed the title fix(routes): unparseable or unknown-provider model name returns 400, not 500 (fixes #252) fix: return 400 for unresolvable model selectors Jul 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/gateway/api/routes/_passthrough.py (1)

226-250: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep raw provider exceptions out of persisted logs

error_log.error_message = str(e) and logger.error(..., e) both carry provider text into storage and logs. /v1/usage and /v1/users/{id}/usage expose error_message, so this can leak upstream details to API consumers as well. Store an opaque error code/class here and log only non-sensitive metadata.

🤖 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 `@src/gateway/api/routes/_passthrough.py` around lines 226 - 250, Replace raw
provider exception text in the passthrough exception handler with the existing
opaque error classification. Compute the error class before constructing
UsageLog, store only that class or code in error_message, and update
logger.error to include non-sensitive metadata without e. Preserve mapped-error
propagation and the existing HTTPException response.

Source: Path instructions

tests/integration/test_passthrough_enforcement.py (1)

25-25: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Import a declared HTTP client dependency here

pyproject.toml doesn’t declare httpx2; this test is only getting it transitively through genai-prices in uv.lock. Import Response from httpx or add httpx2 as a direct test dependency so the test doesn’t hinge on a transitive package.

🤖 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 `@tests/integration/test_passthrough_enforcement.py` at line 25, Update the
import in the passthrough enforcement tests to use the declared httpx dependency
instead of the transitive httpx2 package, unless httpx2 is intentionally added
as a direct test dependency in pyproject.toml. Keep the Response usage
unchanged.
🧹 Nitpick comments (1)
tests/integration/test_batches_endpoint.py (1)

454-456: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Em dash in a comment separator.

# Enforcement — user resolution, budget, rate limiting, ownership (issue #258) uses an em dash as a prose separator. Small thing, easy swap to a colon or hyphen.

As per coding guidelines: "Avoid em dashes and double hyphens as prose separators; use punctuation or rephrase."

✏️ Proposed fix
-# Enforcement — user resolution, budget, rate limiting, ownership (issue `#258`)
+# Enforcement: user resolution, budget, rate limiting, ownership (issue `#258`)
🤖 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 `@tests/integration/test_batches_endpoint.py` around lines 454 - 456, Update
the enforcement section comment separator near the “Enforcement” label to
replace the em dash with a colon or single hyphen, preserving the existing
descriptive text and issue reference.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@src/gateway/api/routes/_passthrough.py`:
- Around line 226-250: Replace raw provider exception text in the passthrough
exception handler with the existing opaque error classification. Compute the
error class before constructing UsageLog, store only that class or code in
error_message, and update logger.error to include non-sensitive metadata without
e. Preserve mapped-error propagation and the existing HTTPException response.

In `@tests/integration/test_passthrough_enforcement.py`:
- Line 25: Update the import in the passthrough enforcement tests to use the
declared httpx dependency instead of the transitive httpx2 package, unless
httpx2 is intentionally added as a direct test dependency in pyproject.toml.
Keep the Response usage unchanged.

---

Nitpick comments:
In `@tests/integration/test_batches_endpoint.py`:
- Around line 454-456: Update the enforcement section comment separator near the
“Enforcement” label to replace the em dash with a colon or single hyphen,
preserving the existing descriptive text and issue reference.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 752c5446-b713-450b-a5c8-32220793cd7a

📥 Commits

Reviewing files that changed from the base of the PR and between 22576f4 and ae9dcde.

📒 Files selected for processing (6)
  • src/gateway/api/routes/_passthrough.py
  • src/gateway/api/routes/_pipeline.py
  • src/gateway/api/routes/batches.py
  • tests/integration/test_alias_api.py
  • tests/integration/test_batches_endpoint.py
  • tests/integration/test_passthrough_enforcement.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/gateway/api/routes/_pipeline.py
  • src/gateway/api/routes/batches.py

@njbrake

njbrake commented Jul 20, 2026

Copy link
Copy Markdown
Member

Note: this comment was drafted by Claude via back-and-forth with @njbrake. The reasoning and decisions are his; the prose is Claude's.

Thanks for this, @AloysJehwin. The diagnosis was spot on: resolve_provider_selector raises ValueError for an unparseable selector and AnyLLMError for an unknown provider, and those were escaping as bare 500s. The _raise_for_unresolvable_model helper and the unit tests were a clean way to fix it.

Heads up on what changed: I've pushed an update to your branch to bring it up to date with main and re-target the fix. While this PR was open, #275 landed and consolidated the pass-through routes (audio, embeddings, images, moderations, rerank) onto a shared run_passthrough helper, so those routes no longer call resolve_provider_selector directly. Your per-route edits no longer applied, and the branch had conflicts in 6 files. That timing was just bad luck, not anything wrong with your patch.

The update keeps your _pipeline.py helper and unit tests as-is (they still cover chat, messages, and responses), and moves the same fix to where resolution now happens:

  • _passthrough.py: guards both resolve_provider_selector calls in run_passthrough, so all the pass-through routes get the 400. On the audio path, the budget reservation is taken before resolution, so it now refunds that reservation before raising the 400 (this closes the budget leak CodeRabbit flagged on your original audio change; the same latent issue had moved into _passthrough.py).
  • batches.py: unified onto your helper so an unknown provider (AnyLLMError), not just an unparseable selector, maps to 400.
  • Added a test in test_passthrough_enforcement.py covering every billable route (including the audio reserve-first paths), and updated test_alias_api.py, which had been asserting the old bare-ValueError behavior your fix corrects.

Locally: lint, mypy, the OpenAPI check, and the full unit + integration suites pass. The PR is now mergeable with a focused 7-file diff. I also tweaked the PR title to the fix: prefix our conventional-commit check wants.

@njbrake
njbrake dismissed their stale review July 20, 2026 09:51

Dismissing my earlier review: the requested changes are in. The branch was updated to main and the 400 fix re-targeted onto run_passthrough and _pipeline; CI (tests, lint, typecheck) is green. Dismissal actioned by Claude at @njbrake's direction.

@AloysJehwin
AloysJehwin temporarily deployed to integration-tests July 20, 2026 10:00 — with GitHub Actions Inactive
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/gateway/api/routes/_passthrough.py 96.51% <100.00%> (ø)
src/gateway/api/routes/_pipeline.py 90.45% <100.00%> (ø)
src/gateway/api/routes/batches.py 85.93% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@njbrake
njbrake merged commit b07a37c into mozilla-ai:main Jul 20, 2026
8 checks passed
@njbrake njbrake mentioned this pull request Jul 22, 2026
4 tasks
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.

fix(routes): unparseable or unknown-provider model name returns a bare 500

3 participants