{"record":{"id":"90b70c1a207b0465","repo":"JuliusBrussee/caveman","slug":"vertex-request-path-q-does-not-name-a-publisher","errorCode":null,"errorMessage":"vertex request path %q does not name a publisher, model, and method","messagePattern":"vertex request path %q does not name a publisher, model, and method","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/vertex/routing.go","lineNumber":35,"sourceCode":"// the families this gateway prices/supports keeps a client from invoking an\n// unpriced partner model whose cost the catalog does not cover.\nvar defaultPublishers = []string{\"google\", \"anthropic\"}\n\n// defaultModelPrefixes is the built-in model-id allowlist, matched as prefixes\n// so versioned ids (gemini-2.5-pro, claude-sonnet-4-5@20250929) are covered.\n// Operators override it with CAVE_VERTEX_MODEL_ALLOWLIST.\nvar defaultModelPrefixes = []string{\"gemini-\", \"claude-\"}\n\n// ResolveUpstreamURL validates the Vertex request shape (publisher + model on\n// the allowlist), enforces SSRF/host constraints against the aiplatform\n// endpoint, and resolves the upstream URL. The /vertex prefix is stripped so the\n// upstream path is the native aiplatform path\n// (/v1/projects/{p}/locations/{l}/publishers/{pub}/models/{model}:{method}). The\n// query string (e.g. ?alt=sse) is preserved unchanged — byte-safe passthrough.\nfunc (a Adapter) ResolveUpstreamURL(ctx context.Context, req *http.Request, route providers.RouteContext) (*url.URL, error) {\n\tpublisher, model, method := parsePredictPath(req.URL.Path)\n\tif publisher == \"\" || model == \"\" || method == \"\" {\n\t\treturn nil, fmt.Errorf(\"vertex request path %q does not name a publisher, model, and method\", req.URL.Path)\n\t}\n\tif !methodAllowed(publisher, method) {\n\t\treturn nil, fmt.Errorf(\"vertex method %q is not allowed for publisher %q\", method, publisher)\n\t}\n\tif !publisherAllowed(publisher) {\n\t\treturn nil, fmt.Errorf(\"vertex publisher %q is not on the allowlist\", publisher)\n\t}\n\tif !modelAllowed(model) {\n\t\treturn nil, fmt.Errorf(\"vertex model %q is not on the allowlist\", model)\n\t}\n\n\tbaseURL := a.BaseURL\n\tif route.BaseURL != \"\" {\n\t\tbaseURL = route.BaseURL\n\t}\n\tbase, err := url.Parse(baseURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"vertex base url invalid: %w\", err)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/proxy/providers/vertex/routing.go#L17-L53","documentation":"ResolveUpstreamURL could not split the client's /vertex request path into the three segments the aiplatform upstream contract requires: publisher, model, and method (expected shape .../publishers/{pub}/models/{model}:{method}). It fires when the path after the /vertex prefix is truncated or malformed, before any allowlist, SSRF, or URL-resolution logic runs.","triggerScenarios":"Thrown at proxy/providers/vertex/routing.go:35 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Fix the request URL so it contains publishers/{publisher}/models/{model}:{method} under the /vertex prefix","Check the client/proxy configuration that built the path for a missing model or method segment"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}