{"record":{"id":"679e1347e3b253a4","repo":"JuliusBrussee/caveman","slug":"bedrock-model-q-is-not-on-the-allowlist","errorCode":null,"errorMessage":"bedrock model %q is not on the allowlist","messagePattern":"bedrock model %q is not on the allowlist","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/bedrock/routing.go","lineNumber":178,"sourceCode":"\n\tkind := endpointKindForPath(req.URL.Path)\n\tif configuredKind := strings.ToLower(strings.TrimSpace(route.EndpointKind)); configuredKind != \"\" && configuredKind != kind {\n\t\treturn nil, fmt.Errorf(\"bedrock configured endpoint kind %q does not allow request kind %q\", configuredKind, kind)\n\t}\n\tswitch kind {\n\tcase endpointRuntime:\n\t\tif !RegionAllowed(region) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock region %q is not on the allowlist\", region)\n\t\t}\n\t\tmodelID, action := parseModelPath(req.URL.Path)\n\t\tif modelID == \"\" || action == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"bedrock request path %q does not name a model and action\", req.URL.Path)\n\t\t}\n\t\tif !actionAllowed(action) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock action %q is not allowed\", action)\n\t\t}\n\t\tif !modelAllowed(modelID) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock model %q is not on the allowlist\", modelID)\n\t\t}\n\t\tbase.Path = strings.TrimRight(base.Path, \"/\") + strings.TrimPrefix(req.URL.Path, \"/bedrock\")\n\tcase endpointMantle:\n\t\tif !env.Bool(\"CAVE_BEDROCK_MANTLE_ENABLED\", false) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock Mantle endpoint is not enabled\")\n\t\t}\n\t\tif !MantleRegionAllowed(region) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock Mantle region %q is not on the allowlist\", region)\n\t\t}\n\t\tif !mantleActionAllowed(req.URL.Path) {\n\t\t\treturn nil, fmt.Errorf(\"bedrock Mantle path %q is not allowed\", req.URL.Path)\n\t\t}\n\t\tbase, err = resolveMantleBase(base, region)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbase.Path = strings.TrimRight(base.Path, \"/\") + strings.TrimPrefix(req.URL.Path, \"/bedrock\")\n\tdefault:","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/bedrock/routing.go#L160-L196","documentation":"ResolveUpstreamURL in the Bedrock adapter validates the model id from a /bedrock/model/{modelId}/{action} request path against the allowlist (built-in prefixes like anthropic.claude-, amazon.nova-, or the CAVE_BEDROCK_MODEL_ALLOWLIST override). The proxy deliberately blocks any model outside this closed list so unverified model ids (and their pricing/availability) cannot be routed to Bedrock. If the model id fails both the identifier validity check and prefix/allowlist matching, the request is rejected before any upstream call.","triggerScenarios":"A POST to /bedrock/model/<modelId>/invoke (or converse, count-tokens, etc.) where modelId does not match a default prefix and is not in CAVE_BEDROCK_MODEL_ALLOWLIST; e.g. a typo'd model id ('anthropic.claude35-sonnet'), a model from another vendor prefix not covered (e.g. 'deepseek.r1'), or an operator-set custom allowlist that omits the model.","commonSituations":"Typo in the model id in client config; using a newly released AWS model before the proxy's built-in prefix list was updated; an operator narrowing CAVE_BEDROCK_MODEL_ALLOWLIST and forgetting to include a model clients still use; expecting ARN-models to pass but supplying a malformed ARN (valid ARNs bypass the prefix list only when the allowlist env is unset).","solutions":["Verify the exact model id in the request path and fix typos (ids are prefix-matched, so 'anthropic.claude-3-5-sonnet-20241022-v2:0' matches 'anthropic.claude-')","Add the model to CAVE_BEDROCK_MODEL_ALLOWLIST (comma-separated, prefix matches allowed) and restart the proxy","If using an inference profile, keep the scope prefix (us./eu./global.) — it is stripped before prefix matching","If the model is new on AWS, upgrade the proxy to a version whose defaultModelPrefixes covers it","If passing a full model ARN, ensure it is well-formed (arn:aws:bedrock:<region>:<account>:foundation-model/...) — a malformed ARN falls back to identifier/prefix checks"],"exampleFix":"// before\nCAVE_BEDROCK_MODEL_ALLOWLIST=anthropic.claude-3-5-sonnet\n// request: /bedrock/model/anthropic.claude-opus-4/invoke -> rejected\n// after\nCAVE_BEDROCK_MODEL_ALLOWLIST=anthropic.claude-3-5-sonnet,anthropic.claude-","handlingStrategy":"validation","validationCode":"func runtimeModelAllowed(modelID string) bool {\n\tfor _, p := range []string{\"anthropic.claude-\", \"amazon.nova-\", \"amazon.titan-\", \"meta.llama2\", \"meta.llama3\", \"meta.llama4\", \"mistral.\", \"cohere.\", \"ai21.\"} {\n\t\tif strings.HasPrefix(modelID, p) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n// call before issuing: if !runtimeModelAllowed(modelID) { fix config }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep a single source of truth for model ids shared between client config and the proxy allowlist","Prefix-check model ids against anthropic.claude-/amazon.nova- etc. before deploying client configs","When narrowing CAVE_BEDROCK_MODEL_ALLOWLIST, diff it against all models your clients actually invoke","Use full valid model ARNs if you need models outside the prefix list (they bypass prefixes when no custom allowlist is set)"],"tags":["bedrock","allowlist","model-validation","config"],"backgroundTag":"invalid-argument-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}