JuliusBrussee/caveman · error
CAVEMAN_AUTH_TOKEN must be at least
Error message
CAVEMAN_AUTH_TOKEN must be at least %d bytes
What it means
CAVEMAN_AUTH_TOKEN is set but shorter than the configured minimum (minAuthTokenBytes). Short tokens are brute-forceable, so Load rejects them during startup rather than accepting weak inbound authentication for the standalone BYOK proxy.
Solutions
- Generate a longer token, e.g. `openssl rand -hex 32`, and set CAVEMAN_AUTH_TOKEN to it.
- Clear the variable entirely (empty string is allowed and means auth disabled) if non-loopback binding is not needed.
- If the value comes from a .env file or shell profile, fix the stale short value there and reload the shell.
Example fix
// before export CAVEMAN_AUTH_TOKEN="abc123" // after export CAVEMAN_AUTH_TOKEN="$(openssl rand -hex 32)"
Defensive patterns
Strategy: validation
Validate before calling
token := os.Getenv("CAVEMAN_AUTH_TOKEN")
if token != "" && len(token) < minAuthTokenBytes {
return fmt.Errorf("CAVEMAN_AUTH_TOKEN must be at least %d bytes", minAuthTokenBytes)
} Prevention
- Generate tokens with openssl rand -hex 32 rather than by hand.
- Never commit placeholder short tokens to .env files.
- Pre-flight check secrets length in deployment scripts.
When it happens
Trigger: Exporting CAVEMAN_AUTH_TOKEN to a string under minAuthTokenBytes bytes (e.g. "abc", "token1") and running config.Load; CI or scripts generating short placeholder secrets.
Common situations: Developer testing locally with a trivial token like "test"; ops checklist leaving a stub value in .env; truncated secret pasted from a secrets manager.
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 contain no spaces or control…
- CAVEMAN_CCR_MAX_BYTES must be a positive integer
- CODEX_HOME points to
- failed to read CODEX_HOME
- Kilo config root resolves to an empty path
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/70d4d9fe4524573f.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/config/config.go:298
selector := (&httpproxy.Config{HTTPProxy: raw, HTTPSProxy: raw, NoProxy: env.String("NO_PROXY", env.String("no_proxy", ""))}).ProxyFunc()
return func(req *http.Request) (*url.URL, error) { return selector(req.URL) }, nil
}
// 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 == "" {View on GitHub (pinned to 3ee70a1026)