{"record":{"id":"3f12b5ae65c7ba57","repo":"JuliusBrussee/caveman","slug":"bedrock-endpoint-host-q-does-not-match-endpoint-k","errorCode":null,"errorMessage":"bedrock endpoint host %q does not match endpoint kind %q","messagePattern":"bedrock endpoint host %q does not match endpoint kind %q","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/bedrock/routing.go","lineNumber":207,"sourceCode":"\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:\n\t\treturn nil, fmt.Errorf(\"bedrock request path %q is not allowed\", req.URL.Path)\n\t}\n\tbase.RawQuery = req.URL.RawQuery\n\n\tif env.IsProduction() {\n\t\tif err := providers.ValidateUpstreamEndpoint(ctx, base, ssrf.ManagedConfig()); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\thostKind, _, ok := bedrockHostKind(base.Hostname())\n\t\tif !ok || hostKind != kind {\n\t\t\treturn nil, fmt.Errorf(\"bedrock endpoint host %q does not match endpoint kind %q\", base.Hostname(), kind)\n\t\t}\n\t}\n\treturn base, nil\n}\n\nfunc endpointKindForPath(path string) string {\n\tswitch {\n\tcase strings.HasPrefix(path, \"/bedrock/model/\"):\n\t\treturn endpointRuntime\n\tcase strings.HasPrefix(path, \"/bedrock/anthropic/\"):\n\t\treturn endpointMantle\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc mantleActionAllowed(path string) bool {\n\tswitch path {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/bedrock/routing.go#L189-L225","documentation":"In production, after building the upstream URL, ResolveUpstreamURL re-derives the endpoint kind from the resolved host (bedrock-runtime.*.amazonaws.com = runtime, bedrock-mantle.*.api.aws = mantle) and requires it to equal the kind implied by the request path. This invariant guarantees routing, signing region, and SSRF validation all target the same surface; a mismatch means the configured base URL or route pointed at the wrong Bedrock host kind.","triggerScenarios":"RouteContext.BaseURL (or the adapter's BaseURL) configured with a Mantle host (bedrock-mantle.<region>.api.aws) while serving a /bedrock/model/... Runtime request, or vice versa; a typo'd/custom base URL that bedrockHostKind cannot classify (ok=false also triggers this); a non-4-label hostname like bedrock-runtime.example.com failing kind detection.","commonSituations":"Operator set route base_url to the Mantle endpoint but clients call the Runtime path (or the reverse); copy-pasting a base URL from Mantle docs into a Runtime route config; self-hosted/test base URLs that pass locally but hit this check when CAVE_ENV indicates production; DNS/CNAME tricks mapping a Bedrock-shaped path to a non-Bedrock host.","solutions":["Align the configured base URL with the request path kind: use https://bedrock-runtime.<region>.amazonaws.com for /bedrock/model/... and https://bedrock-mantle.<region>.api.aws for /bedrock/anthropic/...","Leave route base_url empty to let the adapter derive the correct host from the region","Verify bedrockHostKind can parse the host: exactly 4 DNS labels with bedrock-runtime[-fips] under amazonaws.com or bedrock-mantle under api.aws and an allowlisted region label","Remove any custom base URL override that points a Runtime route at a Mantle host (or vice versa)"],"exampleFix":"// before (caveman.yaml route)\nbase_url: https://bedrock-mantle.us-east-1.api.aws  # Mantle host for a Runtime path\n// after\nbase_url: https://bedrock-runtime.us-east-1.amazonaws.com","handlingStrategy":"validation","validationCode":"func baseMatchesKind(baseURL, kind string) bool {\n\thost := strings.SplitN(strings.TrimPrefix(baseURL, \"https://\"), \"/\", 2)[0]\n\tisMantle := strings.HasPrefix(host, \"bedrock-mantle.\") && strings.HasSuffix(host, \".api.aws\")\n\tisRuntime := strings.HasPrefix(host, \"bedrock-runtime\") && strings.HasSuffix(host, \".amazonaws.com\")\n\tif kind == \"mantle\" { return isMantle }\n\tif kind == \"runtime\" { return isRuntime }\n\treturn false\n}\n// verify route base_url against the path kind before configuring the route","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer leaving route base_url unset so the adapter derives the host from the region","Never mix Mantle hosts with Runtime paths in route config; validate with a unit test per route","Keep hostnames in the canonical 4-label AWS form"],"tags":["bedrock","routing","host-validation","ssrf","production"],"backgroundTag":"internal-invariant-violation","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"}