juanfont/headscale · warning · ErrAPIKeyFailedToParse
failed to parse ApiKey: prefix too short
Error message
failed to parse ApiKey: prefix too short
What it means
Returned by ParseAPIKeyPrefix when the input begins with 'hskey-api-' but the remainder after the prefix is shorter than apiKeyPrefixLength (12 characters). The function needs at least 12 chars to extract the database lookup prefix, so a truncated or hand-mangled key string triggers it.
Source
Thrown at hscontrol/db/api_key.go:175
// Handles formats: "hskey-api-{12chars}-***", "hskey-api-{12chars}", or just "{12chars}".
// Returns the 12-character prefix suitable for database lookup.
func ParseAPIKeyPrefix(displayPrefix string) (string, error) {
// If it's already just the 12-character prefix, return it
if len(displayPrefix) == apiKeyPrefixLength && isValidBase64URLSafe(displayPrefix) {
return displayPrefix, nil
}
// If it starts with the API key prefix, parse it
if strings.HasPrefix(displayPrefix, apiKeyPrefix) {
// Remove the "hskey-api-" prefix
_, remainder, found := strings.Cut(displayPrefix, apiKeyPrefix)
if !found {
return "", fmt.Errorf("%w: invalid display prefix format", ErrAPIKeyFailedToParse)
}
// Extract just the first 12 characters (the actual prefix)
if len(remainder) < apiKeyPrefixLength {
return "", fmt.Errorf("%w: prefix too short", ErrAPIKeyFailedToParse)
}
prefix := remainder[:apiKeyPrefixLength]
// Validate it's base64 URL-safe
if !isValidBase64URLSafe(prefix) {
return "", fmt.Errorf("%w: prefix contains invalid characters", ErrAPIKeyFailedToParse)
}
return prefix, nil
}
// For legacy 7-character prefixes or other formats, return as-is
return displayPrefix, nil
}
// validateAPIKey validates an API key and returns the key if valid.
// Handles both new (hskey-api-{prefix}-{secret}) and legacy (prefix.secret) formats.View on GitHub (pinned to 565fd254d0)
Solutions
- Re-copy the full API key - the display form must be 'hskey-api-' + 12+ chars.
- If passing a bare prefix, pass exactly the 12-character prefix without the 'hskey-api-' decoration.
- Audit scripts that generate or transform key strings for truncation.
Example fix
// before
prefix, err := db.ParseAPIKeyPrefix(truncatedKey)
// after
if strings.HasPrefix(k, "hskey-api-") && len(k) < len("hskey-api-")+12 {
return errors.New("API key was truncated; re-copy the full key")
}
prefix, err := db.ParseAPIKeyPrefix(k) Defensive patterns
Strategy: validation
Validate before calling
const minDisplayLen = len("hskey-api-") + 12 // apiKeyPrefixLength
if strings.HasPrefix(display, "hskey-api-") && len(display) < minDisplayLen {
return errors.New("API key truncated: expected at least 12 chars after the prefix")
}
prefix, err := db.ParseAPIKeyPrefix(display) Type guard
func isPlausibleDisplayKey(s string) bool {
return !strings.HasPrefix(s, "hskey-api-") || len(s) >= len("hskey-api-")+12
} Try / catch
if _, err := db.ParseAPIKeyPrefix(display); err != nil {
if errors.Is(err, db.ErrAPIKeyFailedToParse) {
// reject the input, prompt the user to re-copy the key
}
} Prevention
- Store and pass API keys whole - never substring or rebuild them.
- Validate key length at the API boundary before passing to ParseAPIKeyPrefix.
- Copy keys from 'headscale apikeys create' output directly; avoid re-typing.
When it happens
Trigger: Calling ParseAPIKeyPrefix (or validateAPIKey with a display key) with inputs like 'hskey-api-', 'hskey-api-abc', or a key copied incompletely (truncated by terminal width, clipboard, or log truncation).
Common situations: Users copy-paste an API key that was cut off; scripts that build keys by string concatenation with an empty secret; shell history expansion mangling the key.
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
- failed to parse ApiKey: invalid display prefix format
- failed to parse ApiKey: prefix contains invalid characters
- failed to parse ApiKey
- parsing %s: %w
- HEADSCALE_CLI_API_KEY environment variable needs to be set
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/3fa7ab5196670c46.
Report an issue: GitHub.