{"record":{"id":"6856e612cdb50432","repo":"infiniflow/ragflow","slug":"checkpoint-hmac-key-checkpoint-hmac-key-must-deco","errorCode":null,"errorMessage":"checkpoint HMAC key: CHECKPOINT_HMAC_KEY must decode to exactly 32 bytes, got %d","messagePattern":"checkpoint HMAC key: CHECKPOINT_HMAC_KEY must decode to exactly 32 bytes, got (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/harness/core/interrupt.go","lineNumber":231,"sourceCode":"\thmacLen    = 32\n\tenvHMACKey = \"CHECKPOINT_HMAC_KEY\"\n)\n\n// checkpointHMACKey reads the HMAC key from the CHECKPOINT_HMAC_KEY env var\n// (base64-encoded, 32 bytes). If unset, a random key is generated per startup\n// with a log warning — this is safe for single-process in-memory usage but\n// will BREAK checkpoint resume across process restarts. Production deployments\n// MUST set CHECKPOINT_HMAC_KEY to a stable base64-encoded 32-byte secret.\nvar checkpointHMACKey = loadCheckpointHMACKey()\n\nfunc loadCheckpointHMACKey() []byte {\n\tif env := common.GetEnv(envHMACKey); env != \"\" {\n\t\tk, err := base64.StdEncoding.DecodeString(env)\n\t\tif err != nil {\n\t\t\tpanic(\"checkpoint HMAC key: invalid base64 in \" + envHMACKey + \": \" + err.Error())\n\t\t}\n\t\tif len(k) != 32 {\n\t\t\tpanic(\"checkpoint HMAC key: \" + envHMACKey + \" must decode to exactly 32 bytes, got \" + fmt.Sprintf(\"%d\", len(k)))\n\t\t}\n\t\treturn k\n\t}\n\tk := make([]byte, 32)\n\tif _, err := rand.Read(k); err != nil {\n\t\tpanic(\"failed to generate checkpoint HMAC key: \" + err.Error())\n\t}\n\tcommon.Warn(\"checkpoint HMAC env not set — using random per-process key; checkpoint resume across restarts will fail\", zap.String(\"env\", envHMACKey))\n\treturn k\n}\n\nfunc computeCheckpointHMAC(payload []byte) []byte {\n\tmac := hmac.New(sha256.New, checkpointHMACKey)\n\tmac.Write(payload)\n\treturn mac.Sum(nil)\n}\n\nfunc loadCheckpoint(store CheckPointStore, ctx context.Context, cid string) (context.Context, *runContext, *ResumeInfo, error) {","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/internal/harness/core/interrupt.go#L213-L249","documentation":"The sibling check in loadCheckpointHMACKey: the env value decoded as base64 successfully, but the decoded byte length is not exactly 32. The HMAC construction expects a 256-bit key, so any other length is a fatal configuration error at package-init time. Common causes are keys generated with a different byte length, or double-encoding (base64 of a base64 string).","triggerScenarios":"Setting CHECKPOINT_HMAC_KEY to base64 of 16, 24, or 64 bytes (e.g. openssl rand -base64 16), base64-encoding an already-base64 string (yields ~44+ decoded bytes), or otherwise supplying a correctly encoded key of the wrong length. Panics at startup with the actual decoded length in the message.","commonSituations":"Operator generates a '64-char' or 16-byte secret by habit; secret-management tooling enforces its own key size (e.g. 128-bit) incompatible with the 32-byte requirement; the decoded length reported in the panic message identifies the mismatch.","solutions":["Regenerate with exactly 32 bytes: openssl rand -base64 32, then export it as CHECKPOINT_HMAC_KEY","Read the 'got N' in the panic message to see how far off the length is; ~44 bytes suggests double-encoding — encode the raw secret once","Keep the same 32-byte key across restarts and all replicas; do not rotate per pod","Verify: echo -n \"$CHECKPOINT_HMAC_KEY\" | base64 -d | wc -c  # must print 32"],"exampleFix":"# before\nexport CHECKPOINT_HMAC_KEY=\"$(openssl rand -base64 16)\"  # panic: must decode to exactly 32 bytes, got 16\n\n# after\nexport CHECKPOINT_HMAC_KEY=\"$(openssl rand -base64 32)\"","handlingStrategy":"validation","validationCode":"k, err := base64.StdEncoding.DecodeString(strings.TrimSpace(v))\nif err != nil || len(k) != 32 {\n    return fmt.Errorf(\"CHECKPOINT_HMAC_KEY must be base64 of exactly 32 bytes\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify decoded length with: echo -n \"$CHECKPOINT_HMAC_KEY\" | base64 -d | wc -c","Use exactly openssl rand -base64 32; avoid secret managers that re-encode or enforce other sizes","Pin the same 32-byte key across restarts and replicas so checkpoint resume keeps working"],"tags":["go","hmac","checkpoint","environment-variables","key-length","startup","panic"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}