{"record":{"id":"2089ac33f12833aa","repo":"juanfont/headscale","slug":"w-secret-contains-invalid-characters-expected-b","errorCode":null,"errorMessage":"%w: secret contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)","messagePattern":"%w: secret contains invalid characters \\(expected base64 URL-safe: A-Za-z0-9_-\\)","errorType":"validation","errorClass":"parseErr (ErrPreAuthKeyFailedToParse|ErrOAuthClientFailedToParse|ErrAccessTokenFailedToParse|ErrAPIKeyFailedToParse)","httpStatus":401,"severity":"error","filePath":"hscontrol/db/preauth_keys.go","lineNumber":288,"sourceCode":"\t\treturn \"\", \"\", fmt.Errorf(\n\t\t\t\"%w: secret length mismatch, expected %d chars, got %d\",\n\t\t\tparseErr,\n\t\t\tsecretLen,\n\t\t\tlen(secret),\n\t\t)\n\t}\n\n\t// Validate prefix contains only base64 URL-safe characters\n\tif !isValidBase64URLSafe(prefix) {\n\t\treturn \"\", \"\", fmt.Errorf(\n\t\t\t\"%w: prefix contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)\",\n\t\t\tparseErr,\n\t\t)\n\t}\n\n\t// Validate secret contains only base64 URL-safe characters\n\tif !isValidBase64URLSafe(secret) {\n\t\treturn \"\", \"\", fmt.Errorf(\n\t\t\t\"%w: secret contains invalid characters (expected base64 URL-safe: A-Za-z0-9_-)\",\n\t\t\tparseErr,\n\t\t)\n\t}\n\n\treturn prefix, secret, nil\n}\n\n// isValidBase64URLSafe reports whether s contains only base64 URL-safe\n// characters (A-Za-z0-9-_). Key material is now generated as hex, a subset of\n// this alphabet, so this accepts both current hex keys and any legacy keys\n// still stored in the database.\nfunc isValidBase64URLSafe(s string) bool {\n\treturn !strings.ContainsFunc(s, func(c rune) bool {\n\t\treturn (c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '-' && c != '_'\n\t})\n}\n","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/preauth_keys.go#L270-L306","documentation":"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.","triggerScenarios":"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.","commonSituations":"Single- vs double-quote mistakes in bash (unquoted $ sequences), email/chat autocorrect altering characters, manually retyped keys.","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."],"exampleFix":"// before\nAUTHKEY=hskey-node-abc123def456-secRet$1\nheadscale nodes register --key $AUTHKEY\n\n// after\nAUTHKEY='hskey-node-abc123def456-secRet$1' // single quotes, no expansion\nheadscale nodes register --key \"$AUTHKEY\"","handlingStrategy":"validation","validationCode":"var base64URLSafe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)\nfunc validSecret(s string) bool {\n    return len(s) == 64 && base64URLSafe.MatchString(s)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["preauth-key","validation","key-format","shell-quoting"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}