{"record":{"id":"ac34861407edbab3","repo":"glanceapp/glance","slug":"secret-key-must-be-exactly-d-bytes","errorCode":null,"errorMessage":"secret-key must be exactly %d bytes","messagePattern":"secret-key must be exactly (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/glance/glance.go","lineNumber":68,"sourceCode":"\t\tCreatedAt:  time.Now(),\n\t\tConfig:     *c,\n\t\tslugToPage: make(map[string]*page),\n\t\twidgetByID: make(map[uint64]widget),\n\t}\n\tconfig := &app.Config\n\n\t//\n\t// Init auth\n\t//\n\n\tif len(config.Auth.Users) > 0 {\n\t\tsecretBytes, err := base64.StdEncoding.DecodeString(config.Auth.SecretKey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decoding secret-key: %v\", err)\n\t\t}\n\n\t\tif len(secretBytes) != AUTH_SECRET_KEY_LENGTH {\n\t\t\treturn nil, fmt.Errorf(\"secret-key must be exactly %d bytes\", AUTH_SECRET_KEY_LENGTH)\n\t\t}\n\n\t\tapp.usernameHashToUsername = make(map[string]string)\n\t\tapp.failedAuthAttempts = make(map[string]*failedAuthAttempt)\n\t\tapp.RequiresAuth = true\n\n\t\tfor username := range config.Auth.Users {\n\t\t\tuser := config.Auth.Users[username]\n\t\t\tusernameHash, err := computeUsernameHash(username, secretBytes)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"computing username hash for user %s: %v\", username, err)\n\t\t\t}\n\t\t\tapp.usernameHashToUsername[string(usernameHash)] = username\n\n\t\t\tif user.PasswordHashString != \"\" {\n\t\t\t\tuser.PasswordHash = []byte(user.PasswordHashString)\n\t\t\t\tuser.PasswordHashString = \"\"\n\t\t\t} else {","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/glance.go#L50-L86","documentation":"Startup fails when the base64-decoded auth secret key is not exactly AUTH_SECRET_KEY_LENGTH bytes (32-byte token secret + 32-byte username-hash secret = 64 bytes). The key is split into two HMAC-SHA256 secrets at runtime, so a wrong length would panic on slicing; the check turns that into a clear error.","triggerScenarios":"Decoding succeeds but the plaintext is 63, 65, or any non-64 byte count: e.g. `openssl rand -base64 63`, trimming characters off the key, or decoding a key generated for a different glance version with a different length requirement.","commonSituations":"Using `head -c 63` style shell truncation; regenerating a key after upgrading glance when the length requirement changed; guessing the length instead of copying the documented command.","solutions":["Regenerate with the documented command that yields exactly 64 bytes: `openssl rand -base64 64`","Verify: `echo -n '<key>' | base64 -d | wc -c` must print 64","Do not trim, shorten, or append characters to the key"],"exampleFix":"# verify length\n# echo -n \"$SECRET_KEY\" | base64 -d | wc -c\n64\n# if not 64, regenerate:\n# openssl rand -base64 64\n","handlingStrategy":"validation","validationCode":"b, err := base64.StdEncoding.DecodeString(cfg.Auth.SecretKey)\nif err == nil && len(b) != 64 {\n    return fmt.Errorf(\"secret-key length %d != 64; regenerate with: openssl rand -base64 64\", len(b))\n}\n","typeGuard":null,"tryCatchPattern":"Fail fast at startup with the regenerate command in the error message; no retry is meaningful for a static secret.","preventionTips":["Never truncate or edit generated keys","Include `wc -c` verification in deployment scripts","Regenerate rather than 'fix' a wrong-length key"],"tags":["auth","config","validation","startup"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}