{"record":{"id":"2e20314e1b5fa0a0","repo":"juanfont/headscale","slug":"w-key-too-short-expected-at-least-d-chars-afte","errorCode":null,"errorMessage":"%w: key too short, expected at least %d chars after prefix, got %d","messagePattern":"%w: key too short, expected at least (.+?) chars after prefix, got (.+?)","errorType":"validation","errorClass":"parseErr (ErrPreAuthKeyFailedToParse|ErrOAuthClientFailedToParse|ErrAccessTokenFailedToParse|ErrAPIKeyFailedToParse)","httpStatus":401,"severity":"error","filePath":"hscontrol/db/preauth_keys.go","lineNumber":246,"sourceCode":"\t}\n\n\treturn &pak, nil\n}\n\n// parsePrefixedKey splits the prefix-and-secret portion of a new-format key\n// (the part after the \"hskey-*-\" prefix) into its fixed-length prefix and\n// secret components, validating the length, separator position, and that both\n// components are base64 URL-safe. Fixed-length parsing is used instead of\n// separator-based to handle dashes in base64 URL-safe characters.\nfunc parsePrefixedKey(\n\tprefixAndSecret string,\n\t//nolint:unparam // kept explicit though every credential kind uses a 12-char prefix and 64-char secret today\n\tprefixLen, secretLen int,\n\tparseErr error,\n) (string, string, error) {\n\texpectedMinLength := prefixLen + 1 + secretLen\n\tif len(prefixAndSecret) < expectedMinLength {\n\t\treturn \"\", \"\", fmt.Errorf(\n\t\t\t\"%w: key too short, expected at least %d chars after prefix, got %d\",\n\t\t\tparseErr,\n\t\t\texpectedMinLength,\n\t\t\tlen(prefixAndSecret),\n\t\t)\n\t}\n\n\tprefix := prefixAndSecret[:prefixLen]\n\n\t// Validate separator at expected position\n\tif prefixAndSecret[prefixLen] != '-' {\n\t\treturn \"\", \"\", fmt.Errorf(\n\t\t\t\"%w: expected separator '-' at position %d, got '%c'\",\n\t\t\tparseErr,\n\t\t\tprefixLen,\n\t\t\tprefixAndSecret[prefixLen],\n\t\t)\n\t}","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/db/preauth_keys.go#L228-L264","documentation":"parsePrefixedKey rejected a credential because the string after the 'hskey-*-' scheme prefix is shorter than the minimum of prefixLen + 1 separator + secretLen characters (77 chars for today's 12/64 keys). The library-specific parseErr sentinel is wrapped so callers can classify it. Fixed-length parsing is used deliberately because base64 URL-safe secrets may contain dashes.","triggerScenarios":"Passing a truncated key to any API that parses new-format credentials (pre-auth key verification): a key cut off during copy/paste, a shell variable that swallowed part of the string, or an old/legacy key format with a shorter secret.","commonSituations":"Line wrapping in terminals, chat apps, or YAML files truncating the key; scripts building the key from parts getting the secret length wrong; using an old key generated before the current 64-char secret scheme.","solutions":["Regenerate the key and copy it as a single unbroken line.","Validate length before submitting: after 'hskey-<kind>-', expect exactly 12 chars + '-' + 64 chars.","If using a legacy key, create a new key with the current headscale version."],"exampleFix":"// before\nkey := strings.TrimSpace(cfg.AuthKey)\nnode, err :=client.Up(ctx, key) // fails if truncated\n\n// after\nparts := strings.SplitN(cfg.AuthKey, \"-\", 3) // hskey, kind, rest\nif len(parts) < 3 || len(parts[2]) != 77 {\n    return fmt.Errorf(\"auth key truncated: expected 77 chars after scheme prefix\")\n}","handlingStrategy":"validation","validationCode":"const (\n    prefixLen  = 12\n    secretLen  = 64\n)\nfunc checkKeyShape(rest string) error {\n    if len(rest) < prefixLen+1+secretLen {\n        return fmt.Errorf(\"key truncated: need %d chars after scheme prefix, got %d\", prefixLen+1+secretLen, len(rest))\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate key length client-side before sending a registration request.","Keep auth keys on one line in configs and env vars.","Automate key transport with copy/paste or APIs, never manual transcription."],"tags":["preauth-key","validation","key-format"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}