juanfont/headscale · error · parseErr (ErrPreAuthKeyFailedToParse|ErrOAuthClientFailedToParse|ErrAccessTokenFailedToParse|ErrAPIKeyFailedToParse)

%w: prefix contains invalid characters (expected base64 URL-

Error message

%w: prefix contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)

What it means

The 12-character prefix portion of a parsed credential contains characters outside the base64 URL-safe alphabet (A-Za-z0-9_-). Current headscale generates key material as hex (a subset), so a violation means the prefix was corrupted or is not a headscale key at all. The parseErr sentinel is wrapped for classification.

Source

Thrown at hscontrol/db/preauth_keys.go:280

			prefixAndSecret[prefixLen],
		)
	}

	secret := prefixAndSecret[prefixLen+1:]

	// Validate secret length
	if len(secret) != secretLen {
		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

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Confirm the key starts with the 'hskey-' scheme and was issued by this headscale instance.
  2. If the key passed through a URL or form, ensure it was not percent-encoded or truncated.
  3. Regenerate the key from the headscale CLI/API and use it directly.
Defensive patterns

Strategy: validation

Validate before calling

var base64URLSafe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
func validPrefix(p string) bool {
    return len(p) == 12 && base64URLSafe.MatchString(p)
}

Prevention

When it happens

Trigger: Passing a non-headscale credential (e.g. a Tailscale authkey 'tskey-auth-...') into headscale's key parser, or a prefix mangled by encoding (spaces, '%', '=' from URL encoding).

Common situations: Mixing up headscale and Tailscale keys in configs; URL-encoding or HTML-entity corruption when keys travel through web forms or query parameters.

Understand the failure class

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/5047e6bacc4d4041. Report an issue: GitHub.