Repository navigation
Description
Checks
- I have updated to the lastest minor and patch version of Strands
- I have checked the documentation and this is not expected behavior
- I have searched ./issues and there are no duplicates of my issue
SDK Language
TypeScript
Strands Version
Reported against current main / latest published @strands-agents/sdk. Exact version not captured from the reporter — the routing defect below is present on main as of this filing.
Language Runtime Version
Not captured from the reporter (Node.js).
Operating System
Not captured from the reporter.
Installation Method
npm
Steps to Reproduce
Reported by a user attempting to reach xai.grok-4.3 through the Bedrock Mantle endpoint in us-east-1 (https://bedrock-mantle.us-east-1.api.aws).
import { OpenAIModel } from '@strands-agents/sdk/models/openai'
const model = new OpenAIModel({
modelId: 'xai.grok-4.3',
bedrockMantleConfig: { region: 'us-east-1' },
})
await model.stream([{ role: 'user', content: [{ text: 'hello' }] }])The model id is valid and servable-by-catalog: GET /v1/models on the same endpoint returns xai.grok-4.3 with "status":"available", so this is not a typo'd or unavailable model id.
The reporter also exercised the three routes directly with curl to isolate the SDK from the service:
| Route | Payload shape | HTTP | Response |
|---|---|---|---|
/v1/chat/completions |
Chat Completions | 400 | {"error":{"code":"validation_error","message":"model 'xai.grok-4.3' isn't supported on this route","type":"invalid_request_error"}} |
/openai/v1/chat/completions |
Chat Completions | 500 | {"error":{"code":"internal_server_error","message":"The server had an error while processing your request. Sorry about that!","type":"server_error"}} |
/openai/v1/responses |
Responses API | 500 | same internal_server_error body |
Control case, same account, same credentials, same session: google.gemma-4-31b returns valid completions on /openai/v1/chat/completions and /openai/v1/responses. That rules out credentials, account permissions, and client code.
Expected Behavior
Two separate expectations, because there are two separate defects here — one ours, one not.
OpenAIModel+bedrockMantleConfigshould route a non-OpenAI-family Mantle model id (xai.*,google.*,anthropic.*) to the base path Mantle actually serves it from. It should not construct a request against a route the service will reject withvalidation_error.- A model advertised as
"status":"available"byGET /v1/modelsshould be invocable on at least one route.
Actual Behavior
Defect 1 (SDK, actionable here): the Mantle base-path table only special-cases openai.gpt-5.*, so every third-party model id falls through to /v1 — the route that 400s.
strands-ts/src/models/openai/mantle.ts:
const OPENAI_PATH_MODEL_PREFIXES = ['openai.gpt-5.'] as const
export function bedrockMantleBaseUrl(region: string, modelId: string): string {
const suffix = OPENAI_PATH_MODEL_PREFIXES.some((prefix) => modelId.startsWith(prefix)) ? '/openai/v1' : '/v1'
return `https://bedrock-mantle.${region}.api.aws${suffix}`
}xai.grok-4.3 does not start with openai.gpt-5., so it resolves to /v1 — and /v1/chat/completions is precisely the route that returns model 'xai.grok-4.3' isn't supported on this route. The SDK cannot currently be pointed at /openai/v1 for this model either, since bedrockMantleConfig rejects a caller-supplied clientConfig.baseURL (validated in mantle.test.ts, "throws when clientConfig.baseURL is set alongside bedrockMantleConfig"). There is no supported escape hatch.
The reporter's own control case is the proof that the table is wrong, not just incomplete: google.gemma-4-31b works on /openai/v1, but the SDK would route it to /v1 for the same reason. So the mis-routing is not specific to Grok — it applies to every non-openai.gpt-5.* id, and it is currently masked only because those models aren't documented as supported yet (#3563).
The comment in mantle.ts states the operative rule correctly ("the base path is keyed by model family, not API surface") but the prefix list encodes only the OpenAI families that existed when it was written.
Defect 2 (Mantle service-side, not fixable in this repo): /openai/v1/* 500s for xai.grok-4.3.
Once routed to /openai/v1, Grok returns internal_server_error on both Chat Completions and Responses, while Gemma succeeds on the same routes with the same credentials. That is a serving-side problem with this specific model, either a catalog mis-registration (advertised available, not actually servable) or a backend fault. Fixing Defect 1 will not make xai.grok-4.3 work; it will change a 400 into a 500 until the service side is resolved. Worth cutting a separate ticket to the Bedrock Mantle service team, and worth confirming Grok is servable before wiring it up here — otherwise SDK-side support cannot be integ-tested.
Additional Context
Parity: the Python SDK has the identical defect at strands-py/src/strands/models/_openai_bedrock.py:
_OPENAI_PATH_MODEL_PREFIXES: tuple[str, ...] = ("openai.gpt-5.",)
def _resolve_mantle_base_path(model_id: str) -> str:
if model_id.startswith(_OPENAI_PATH_MODEL_PREFIXES):
return "/openai/v1"
return "/v1"Any fix should land in both SDKs to preserve parity.
Related history on this same base-path logic, all of which suggests the prefix-matching approach is fragile enough to be worth replacing rather than extending:
- [BUG] OpenAIModel Bedrock Mantle base URL is wrong for the Responses API (hits /v1/responses) #3279 —
[BUG] OpenAIModel Bedrock Mantle base URL is wrong for the Responses API (hits /v1/responses)(closed) - bedrock_mantle_config builds wrong base_url for OpenAI Responses models (missing /openai path segment) #2629 —
bedrock_mantle_config builds wrong base_url for OpenAI Responses models (missing /openai path segment)(closed) - OpenAIModel (Responses, TypeScript) sends invalid assistant history shape, rejected by Bedrock Mantle #3388 —
OpenAIModel (Responses, TypeScript) sends invalid assistant history shape, rejected by Bedrock Mantle(closed)
This is the fourth bug filed against Mantle base-URL/route derivation.
Possible Solution
Scoped to Defect 1 only.
The narrow fix is to add the third-party families to the prefix list so they resolve to /openai/v1:
const OPENAI_PATH_MODEL_PREFIXES = ['openai.gpt-5.', 'xai.', 'google.', 'anthropic.'] as constThat is a one-line change, mirrored in Python, and it makes the confirmed-working case (google.gemma-4-31b on /openai/v1) actually reachable through the SDK. But it keeps the design that has now produced four bugs: a hardcoded allowlist of prefixes that must be updated every time Mantle onboards a family, where the failure mode is a confusing service-side 400 rather than a clear client-side error.
Two alternatives worth weighing during triage:
- Invert the default.
/openai/v1appears to be the general case and/v1the exception (openai.gpt-oss-*). Encoding the exceptions instead of the rule shrinks the list that needs maintenance and makes new families work by default. - Fail loudly on unrecognized families. If the model id matches no known Mantle family, throw a client-side error naming the id and the supported prefixes rather than silently building a URL that the service will reject. That converts an opaque
validation_errorfrom Mantle into an actionable message, which is what the reporter needed here.
Either way, extending the allowlist without also deciding on the default is likely to produce bug number five.
Also worth considering: whether bedrockMantleConfig's hard rejection of clientConfig.baseURL should relax to a documented override. The validation is sound as a default (it prevents accidentally unsigned or mis-pointed requests), but with no escape hatch, a routing-table gap is a hard block rather than a workaround-able annoyance.
Related Issues
#3563 (feature request: Mantle-API support for Anthropic, Google and Grok models). This issue is the bug half of that request: the routing table silently mis-routes those families today rather than rejecting them, so the failure surfaces as an opaque service 400.
Metadata
Metadata
Assignees
Labels
Type
Fields
Language
Activity
yonib05 commentedon Aug 5, 2026
@strandly-the-agent can you please verify this bug?
strandly-the-agent commentedon Aug 5, 2026
🔎 Automated Bug Verification
Defect 1 reproduced on
main(2f90779), in both SDKs. The report's snippet as written issues zero HTTP requests (stream()is an async generator), so I instrumented it to capture the outgoing URL — formodelId: 'xai.grok-4.3'it hits:Full matrix:
xai.*,google.*,anthropic.*→/v1; onlyopenai.gpt-5.*→/openai/v1(openai.gpt-oss-120b→/v1, as intended). Python is identical for bothOpenAIModelandOpenAIResponsesModel. No escape hatch:clientConfig.baseURL/client_args['base_url']are both rejected alongside the Mantle config. Confirmed at the unit level too (bedrockMantleBaseUrl/_resolve_mantle_base_pathcalled directly, no stubs), so the URL isn't an artifact of the test harness.Defect 2 not verified, and it gates the fix. This sandbox's role has no
bedrock-mantlepermissions — all four routes returned401 access_denied— so the 400/500 responses and thegoogle.gemma-4-31bcontrol case rest on the reporter's curl evidence alone. That control case is the whole justification for routing these families to/openai/v1; someone with Mantle access should re-confirm it before landing the prefix-list change, otherwise the "fix" swaps one wrong route for another.Reproduction (TypeScript) + captured output
Routing matrix (same capture, TypeScript, both API surfaces):
Python parity (same five ids,
httpx.MockTransportcapture,PYTHONPATH=strands-py/src):Environment:
@strands-agents/sdkatmain2f90779 · Node v22.14.0,openai6.45.0 · CPython 3.12.13 · Linux aarch64 sandbox. Deterministic across repeated runs; independently re-run by a second agent with no other context.Version skew: none. The prefix list is unchanged since it was introduced — TS
d1fbd75(#3280, closed #3279) and Python2399728(#3032, closed #2629); #3412 touched the file for region validation only. The unit suite assertsopenai.gpt-5.*→/openai/v1andopenai.gpt-oss-120b→/v1, and has no case for the third-party families — untested territory rather than a broken test.Live probe (blocked):
GET /v1/models,POST /v1/chat/completions,POST /openai/v1/chat/completions,POST /openai/v1/responsesall returned401 access_denied(bedrock-mantle:ListModels/bedrock-mantle:CreateInferencenot allowed for the sandbox role). No AWS resources created.Automated triage. This reproduction is a starting point, not a final determination.
yonib05 commentedon Aug 5, 2026
✅ Defect 2 does not reproduce — the 500s were transient and are now resolved
Verified against live Bedrock Mantle in
us-east-1(account with realbedrock-mantlepermissions, bearer token minted the same way the SDK does: SigV4 presign ofbedrock.amazonaws.com/?Action=CallWithBearerToken).xai.grok-4.3now serves HTTP 200 on both/openai/v1routes:So the
internal_server_errorthe reporter hit on/openai/v1/*was a transient serving-side fault, not a catalog mis-registration. No service ticket needed. The model is genuinely servable, and it is servable on exactly the route the SDK does not send it to.The reporter's control case also replicates:
google.gemma-4-31b→ 200 on both/openai/v1routes, 400 on/v1. That confirms the evidence @strandly-the-agent flagged as unverified, so the fix is no longer gated on it.Net effect: Defect 1 is now the whole bug, and it is the only thing standing between
xai.grok-4.3and working. Grok on Mantle is broken through the SDK purely because of our base-path resolution.strandly-the-agent commentedon Aug 5, 2026
Thanks — that closes the gap I flagged. One probe is left, and it decides the shape of the fix: does
openai.gpt-oss-120balso serve on/openai/v1?With grok and gemma now confirmed 200 on
/openai/v1and 400 on/v1,openai.gpt-oss-*is the only known id that needs/v1— and it is the only Mantle id exercised anywhere in the repo (strands-ts/test/integ/models/openai/mantle.test.node.ts:16,strands-py/tests_integ/models/test_model_mantle.py:16, and both docs snippets all useopenai.gpt-oss-120b). So:/openai/v1→ delete the table. One base path for everything, and this class of bug cannot recur./openai/v1→ it is a genuine exception: invert the default (/openai/v1unless the id starts withopenai.gpt-oss-), so new families work by default and only the exception list needs maintenance.Extending the allowlist is the one option that's wrong either way. Note the current prefix is
'openai.gpt-5.'with the trailing dot — it only matches thegpt-5.xform, so an OpenAI id in any other shape already falls through to/v1alongside every un-listed third-party family.Whichever way it goes, worth pairing the fix with an integ test on a non-gpt-oss id —
google.gemma-4-31bis now a validated target. Integ coverage being 100% gpt-oss is exactly why this gap went unnoticed for four issues.yonib05 commentedon Aug 5, 2026
Fix up in #3666
Verified against live Mantle in
us-east-1, and the investigation changed the fix.The prefix-list fix I proposed in this issue was wrong. Adding
'xai.','google.','anthropic.'toOPENAI_PATH_MODEL_PREFIXESwould have traded one bug for another, because the base path is a property of the individual model, not of its vendor prefix:google.gemma-4-31b/openai/v1google.gemma-3-27b-it/v1Same prefix, different base paths. A
google.prefix would have mis-routed all three Gemma 3 models. #3666 uses an explicit id set instead, with prefixes kept only for families that have no/v1members.Derived by invoking all 55 models listed in
us-east-1across both base paths and both API surfaces. Anthropic turned out to be a third case: those models are served from/anthropic/v1/messages, a different protocol reached viaAnthropicModel, so they answer on neither OpenAI-compatible path and are out of scope for this table.On catching this early. Route support is not discoverable:
GET /v1/modelsreportsstatusbut not routing, and there is no/openai/v1/models, so any table here is hand-maintained and goes stale on each Mantle onboarding. #3666 adds an integ test to each SDK that replays the probe against the live catalog and fails naming any mis-routed id. I confirmed it works by reverting the table to its pre-fix state:Both SDKs fixed. All 11 table ids plus 6
/v1controls verified end to end through Python and TypeScript.@strandly-the-agent thanks for flagging that the control case was unverified: that turned out to be the load-bearing detail, and probing it properly is what surfaced the Gemma 3/4 split.
test(models): make the Mantle drift detector fail on ambiguity, not j…