JuliusBrussee/caveman · error
CAVEMAN_AUTH_TOKEN must contain no spaces or control…
Error message
CAVEMAN_AUTH_TOKEN must contain no spaces or control characters
What it means
Config validation error in the proxy's Load path: CAVEMAN_AUTH_TOKEN contains spaces or control characters. Control bytes would terminate the HTTP header field and a space would split scheme from value in 'Authorization: Bearer <token>'; the message deliberately never echoes the secret.
Solutions
- Remove spaces and control characters from the token value (trim trailing newline/whitespace).
- Regenerate with `openssl rand -hex 32`, which only produces hex digits.
- Check the .env file for CRLF line endings (dos2unix) and remove surrounding quotes/whitespace.
Example fix
// before export CAVEMAN_AUTH_TOKEN="$(cat token.txt)" # file ends with newline // after export CAVEMAN_AUTH_TOKEN="$(tr -d '[:space:]' < token.txt)"
Defensive patterns
Strategy: validation
Validate before calling
token := strings.TrimSpace(os.Getenv("CAVEMAN_AUTH_TOKEN"))
for _, r := range token {
if r == ' ' || r < 0x20 || r == 0x7f {
return fmt.Errorf("CAVEMAN_AUTH_TOKEN contains space or control character at %q", token)
}
} Prevention
- Trim generated secrets before assigning them to env vars.
- Save .env files with LF endings, not CRLF.
- Use hex/base64 token generators that emit no whitespace.
When it happens
Trigger: Setting CAVEMAN_AUTH_TOKEN to a value with internal spaces ("my secret token"), trailing whitespace/newline (e.g. from `echo` without -n, or a CRLF line ending in .env), or an embedded tab.
Common situations: Pasting a secret with a trailing newline; generating tokens with a tool that appends a newline; quoting mistakes in shell assignment leaving literal spaces; Windows-edited config files with CRLF.
Understand the failure class
Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.
Related errors
- CAVEMAN_AUTH_TOKEN must be at least
- bedrock Mantle endpoint is not enabled
- cannot preserve opencode inline configuration; launching…
- cannot safely resolve Qwen's effective OPENAI_API_KEY
- cave_auth_required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/95644171ab89b8e0.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/config/config.go:302
// minAuthTokenBytes is the floor for the inbound shared secret. The token is the
// only gate in front of every configured provider credential once the proxy is
// reachable off-host, so a short one is not a weaker deployment, it is an open one.
const minAuthTokenBytes = 16
// validateAuthToken refuses a token that cannot survive one HTTP header value:
// control bytes terminate the field, and a space would split scheme from value in
// `Authorization: Bearer <token>`. The error never echoes the value — it is a
// secret and this message reaches the proxy log.
func validateAuthToken(token string) error {
if token == "" {
return nil
}
if len(token) < minAuthTokenBytes {
return fmt.Errorf("CAVEMAN_AUTH_TOKEN must be at least %d bytes", minAuthTokenBytes)
}
for _, r := range token {
if r == ' ' || r < 0x20 || r == 0x7f {
return fmt.Errorf("CAVEMAN_AUTH_TOKEN must contain no spaces or control characters")
}
}
return nil
}
// validateListen keeps standalone's BYOK proxy local to one operator unless an
// inbound credential gates it. Binding an empty, wildcard, or non-loopback host
// would expose every configured provider credential to the network with no
// inbound authentication; authenticated says CAVEMAN_AUTH_TOKEN is set, so
// standalone.Auth rejects every request that does not present it and the wider
// bind becomes a deliberate operator choice instead of an accident.
func validateListen(listen string, authenticated bool) error {
host, port, err := net.SplitHostPort(strings.TrimSpace(listen))
if err != nil || port == "" {
return fmt.Errorf("listen address %q must be loopback host:port", listen)
}
if strings.EqualFold(host, "localhost") {
return nilView on GitHub (pinned to 3ee70a1026)