JuliusBrussee/caveman · error

bedrock base url invalid: %w

Error message

bedrock base url invalid: %w

What it means

The Bedrock adapter parses its base URL (route.BaseURL if set, else the adapter default) before routing, and a malformed value — one that url.Parse rejects — produces this wrapped parse error. Unlike the base adapter, Bedrock needs a parsed URL early because region and endpoint-kind resolution derive from it (runtime vs mantle paths).

Source

Thrown at proxy/providers/bedrock/routing.go:154

)

// ResolveUpstreamURL validates the endpoint kind, region, action, and model
// resource, then resolves one of two explicit Bedrock surfaces:
//
//   - Runtime: /bedrock/model/... -> bedrock-runtime.../model/...
//   - Mantle:  /bedrock/anthropic/v1/messages -> bedrock-mantle.../anthropic/...
//
// Mantle stays deployment-opt-in because its IAM service, model coverage,
// quotas, features, stream wire, and AWS invocation-logging support differ from
// Runtime. Both surfaces remain provider=bedrock in telemetry.
func (a Adapter) ResolveUpstreamURL(ctx context.Context, req *http.Request, route providers.RouteContext) (*url.URL, error) {
	baseURL := a.BaseURL
	if route.BaseURL != "" {
		baseURL = route.BaseURL
	}
	base, err := url.Parse(baseURL)
	if err != nil {
		return nil, fmt.Errorf("bedrock base url invalid: %w", err)
	}
	region, err := resolveRegion(req, base)
	if err != nil {
		return nil, err
	}

	kind := endpointKindForPath(req.URL.Path)
	if configuredKind := strings.ToLower(strings.TrimSpace(route.EndpointKind)); configuredKind != "" && configuredKind != kind {
		return nil, fmt.Errorf("bedrock configured endpoint kind %q does not allow request kind %q", configuredKind, kind)
	}
	switch kind {
	case endpointRuntime:
		if !RegionAllowed(region) {
			return nil, fmt.Errorf("bedrock region %q is not on the allowlist", region)
		}
		modelID, action := parseModelPath(req.URL.Path)
		if modelID == "" || action == "" {
			return nil, fmt.Errorf("bedrock request path %q does not name a model and action", req.URL.Path)

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Fix the base_url value to a well-formed absolute URL, e.g. https://bedrock-runtime.us-east-1.amazonaws.com.
  2. Validate at config load: if _, err := url.Parse(v); err != nil { return fmt.Errorf("bad base_url: %w", err) }.
  3. Inspect the raw config/env for hidden characters (cat -A) after templating.

Example fix

# before
base_url: "https://bedrock-runtime.us-east-1.amazonaws.com:%zz"

# after
base_url: "https://bedrock-runtime.us-east-1.amazonaws.com"
Defensive patterns

Strategy: validation

Validate before calling

base := strings.TrimSpace(route.BaseURL)
if base == "" {
    base = strings.TrimSpace(adapterBaseURL)
}
if _, err := url.Parse(base); err != nil {
    return fmt.Errorf("bedrock base_url %q is not parseable: %w", base, err)
}

Type guard

func isParseableURL(raw string) bool {
    _, err := url.Parse(strings.TrimSpace(raw))
    return err == nil
}

Try / catch

if _, err := adapter.ResolveUpstreamURL(ctx, req, route); err != nil {
    if strings.Contains(err.Error(), "base url invalid") {
        http.Error(w, "bedrock base_url is malformed; fix provider config", http.StatusBadGateway)
        return
    }
    http.Error(w, err.Error(), http.StatusBadRequest)
}

Prevention

When it happens

Trigger: Configuring base_url with invalid URL syntax: unmatched percent-encoding ('%zz'), a control character, malformed port ('https://host:port'), or stray whitespace past trimming.

Common situations: Hand-edited YAML with a truncated URL; env interpolation inserting an empty or partial value; a pasted URL containing a literal newline; template engines emitting bad percent-escapes.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/c8659c4142f5a2a4. Report an issue: GitHub.