{"record":{"id":"0fdfb3aca8fd09e2","repo":"infiniflow/ragflow","slug":"checkpoint-hmac-key-invalid-base64-in-checkpoint","errorCode":null,"errorMessage":"checkpoint HMAC key: invalid base64 in CHECKPOINT_HMAC_KEY: {err}","messagePattern":"checkpoint HMAC key: invalid base64 in CHECKPOINT_HMAC_KEY: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"internal/harness/core/interrupt.go","lineNumber":228,"sourceCode":"// ---- Checkpoint integrity (HMAC) ----\n\nconst (\n\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)","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/internal/harness/core/interrupt.go#L210-L246","documentation":"loadCheckpointHMACKey runs at package initialization (var checkpointHMACKey = loadCheckpointHMACKey()) and panics when the CHECKPOINT_HMAC_KEY env var is set but is not valid standard base64. The key (32 bytes, base64-encoded) is used to HMAC checkpoint payloads; treating malformed input as a fatal startup error prevents silently running with a bogus key that would break checkpoint integrity verification.","triggerScenarios":"Setting CHECKPOINT_HMAC_KEY to a raw (non-base64) 32-byte string, a hex-encoded key, a base64 URL-safe-encoded value using -/_ instead of +//, or a value with trailing whitespace/newline or a typo — any input base64.StdEncoding.DecodeString rejects. The panic fires at process startup, before serving.","commonSituations":"Operators paste `openssl rand -hex 32` output (hex, not base64) into the env var; a shell here-doc or YAML block scalar appends a newline; a secret manager injects urlsafe-base64; copy-paste drops or mangles trailing '=' padding.","solutions":["Generate a correct value: openssl rand -base64 32 and set CHECKPOINT_HMAC_KEY to its output","Strip any trailing newline/whitespace: export CHECKPOINT_HMAC_KEY=\"$(printf %s \"$CHECKPOINT_HMAC_KEY\")\"","If the secret is urlsafe base64, re-encode it to standard base64 (replace - with +, _ with /) or store it decoded","Verify with: echo -n \"$CHECKPOINT_HMAC_KEY\" | base64 -d | wc -c  # must print 32"],"exampleFix":"# before\nexport CHECKPOINT_HMAC_KEY=\"a1b2c3...\"      # raw hex -> panic: invalid base64\n\n# after\nexport CHECKPOINT_HMAC_KEY=\"$(openssl rand -base64 32)\"","handlingStrategy":"validation","validationCode":"if v := os.Getenv(\"CHECKPOINT_HMAC_KEY\"); v != \"\" {\n    if _, err := base64.StdEncoding.DecodeString(strings.TrimSpace(v)); err != nil {\n        return fmt.Errorf(\"CHECKPOINT_HMAC_KEY must be standard base64\")\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate the key only with openssl rand -base64 32 — never hex or urlsafe encoding","Trim whitespace when loading secrets from files or YAML block scalars","Add a startup config lint (decode + length check) to deployment pipelines"],"tags":["go","hmac","checkpoint","environment-variables","base64","startup","panic"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}