juanfont/headscale · error · parseErr (ErrPreAuthKeyFailedToParse|ErrOAuthClientFailedToParse|ErrAccessTokenFailedToParse|ErrAPIKeyFailedToParse)
%w: secret contains invalid characters (expected base64 URL-
Error message
%w: secret contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)
What it means
The 64-character secret portion of a parsed credential contains characters outside the base64 URL-safe alphabet (A-Za-z0-9_-). Headscale generates secrets as hex, so invalid characters indicate corruption, replacement (e.g. shell expansion), or a foreign key format. The parseErr sentinel is wrapped for classification.
Source
Thrown at hscontrol/db/preauth_keys.go:288
return "", "", fmt.Errorf(
"%w: secret length mismatch, expected %d chars, got %d",
parseErr,
secretLen,
len(secret),
)
}
// Validate prefix contains only base64 URL-safe characters
if !isValidBase64URLSafe(prefix) {
return "", "", fmt.Errorf(
"%w: prefix contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)",
parseErr,
)
}
// Validate secret contains only base64 URL-safe characters
if !isValidBase64URLSafe(secret) {
return "", "", fmt.Errorf(
"%w: secret contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)",
parseErr,
)
}
return prefix, secret, nil
}
// isValidBase64URLSafe reports whether s contains only base64 URL-safe
// characters (A-Za-z0-9-_). Key material is now generated as hex, a subset of
// this alphabet, so this accepts both current hex keys and any legacy keys
// still stored in the database.
func isValidBase64URLSafe(s string) bool {
return !strings.ContainsFunc(s, func(c rune) bool {
return (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '-' && c != '_'
})
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Always quote the key in single quotes in shell commands.
- Regenerate the key and transfer it via copy/paste without retyping.
- Verify with a quick regex that the secret matches ^[A-Za-z0-9_-]{64}$ before use.
Example fix
// before AUTHKEY=hskey-node-abc123def456-secRet$1 headscale nodes register --key $AUTHKEY // after AUTHKEY='hskey-node-abc123def456-secRet$1' // single quotes, no expansion headscale nodes register --key "$AUTHKEY"
Defensive patterns
Strategy: validation
Validate before calling
var base64URLSafe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
func validSecret(s string) bool {
return len(s) == 64 && base64URLSafe.MatchString(s)
} Prevention
- Single-quote keys in shell commands to prevent $ expansion.
- Disable autocorrect/autocapitalize on fields that receive keys.
- Validate against ^[A-Za-z0-9_-]{64}$ before submitting.
When it happens
Trigger: Shell scripts where parts of the key were interpreted ($ expansion historically mangled old-format keys), keys passed through systems that transliterate characters (e.g. '0'/'O' swaps by humans), or non-headscale credentials fed to the parser.
Common situations: Single- vs double-quote mistakes in bash (unquoted $ sequences), email/chat autocorrect altering characters, manually retyped keys.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- %w: key too short, expected at least %d chars after prefix,
- %w: expected separator '-' at position %d, got '%c'
- %w: secret length mismatch, expected %d chars, got %d
- %w: prefix contains invalid characters (expected base64 URL-
- failed to parse auth-key
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/2089ac33f12833aa.
Report an issue: GitHub.