JuliusBrussee/caveman · error

compat upstream name

Error message

compat upstream name %q must match [a-z0-9][a-z0-9._-]{0,63}

What it means

ValidateName enforces the mount-name grammar [a-z0-9][a-z0-9._-]{0,63}: lowercase alphanumeric first character, then lowercase alphanumerics, dots, underscores, or hyphens, max 64 chars. Names outside this grammar fail with this message because the name becomes a URL path segment (/compat/<name>/) and must be safe and unambiguous.

Solutions

  1. Rename the mount to lowercase alphanumerics with . _ - separators, e.g. "claude-prod".
  2. Ensure the first character is a lowercase letter or digit.
  3. Shorten names to 64 characters or fewer.
  4. Slugify the name (strings.ToLower + replace invalid chars) in config generation tooling.

Example fix

# before
name = "Claude_Prod EU"
# after
name = "claude-prod-eu"
Defensive patterns

Strategy: validation

Validate before calling

var validName = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,63}$`)
if !validName.MatchString(mount.Name) {
    return fmt.Errorf("mount name %q must match [a-z0-9][a-z0-9._-]{0,63}", mount.Name)
}

Try / catch

if err := openaicompat.ValidateName(name); err != nil {
    return nil, fmt.Errorf("rename mount: %w", err)
}

Prevention

When it happens

Trigger: A mount name with uppercase letters, spaces, a leading dot/hyphen/underscore, non-ASCII characters, or length > 64 is passed to NewNamedWithWireDialect or listed in config and checked by validateCompat.

Common situations: Human-readable names like "My Upstream" or "Claude_Prod"; names auto-generated with a UUID prefix; env-interpolated names carrying whitespace.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/e4c2384cf22e77ef. Report an issue: GitHub.

Appendix: source

Thrown at proxy/providers/openaicompat/openaicompat.go:422

			return fmt.Errorf("header %q cannot be forwarded", name)
		}
		switch lower {
		// Routing/framing, credentials, and fields whose value this proxy
		// constructs. x-forwarded-*/forwarded/x-real-ip would let a caller
		// choose the client address an upstream rate-limits or allowlists on.
		case "host", "connection", "keep-alive", "proxy-connection", "proxy-authorization", "proxy-authenticate", "te", "trailer", "transfer-encoding", "upgrade", "content-length", "expect",
			"authorization", "x-api-key", "api-key", "x-goog-api-key", "x-goog-user-project", "cookie", "set-cookie",
			"forwarded", "x-forwarded-for", "x-forwarded-host", "x-forwarded-proto", "x-forwarded-port", "x-real-ip",
			"user-agent", "content-type":
			return fmt.Errorf("header %q cannot be forwarded", name)
		}
	}
	return nil
}

func ValidateName(name string) error {
	if !validName.MatchString(name) {
		return fmt.Errorf("compat upstream name %q must match [a-z0-9][a-z0-9._-]{0,63}", name)
	}
	switch name {
	case "stub", "openai-compatible":
		return fmt.Errorf("compat upstream name %q is reserved", name)
	default:
		return nil
	}
}

func ValidateBaseURL(raw string) error {
	_, err := parseBaseURL(raw, "compat")
	return err
}

// ResolveUpstreamURL intentionally ignores RouteContext.BaseURL. Named mounts
// are configured as independent static upstreams (`compat:` in standalone or
// CAVE_COMPAT_UPSTREAMS in managed mode); applying the generic project
// openai_compatible override here would collapse every named route onto one

View on GitHub (pinned to 3ee70a1026)