infiniflow/ragflow · critical
checkpoint HMAC key: CHECKPOINT_HMAC_KEY must decode to exac
Error message
checkpoint HMAC key: CHECKPOINT_HMAC_KEY must decode to exactly 32 bytes, got %d
What it means
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).
Source
Thrown at internal/harness/core/interrupt.go:231
hmacLen = 32
envHMACKey = "CHECKPOINT_HMAC_KEY"
)
// checkpointHMACKey reads the HMAC key from the CHECKPOINT_HMAC_KEY env var
// (base64-encoded, 32 bytes). If unset, a random key is generated per startup
// with a log warning — this is safe for single-process in-memory usage but
// will BREAK checkpoint resume across process restarts. Production deployments
// MUST set CHECKPOINT_HMAC_KEY to a stable base64-encoded 32-byte secret.
var checkpointHMACKey = loadCheckpointHMACKey()
func loadCheckpointHMACKey() []byte {
if env := common.GetEnv(envHMACKey); env != "" {
k, err := base64.StdEncoding.DecodeString(env)
if err != nil {
panic("checkpoint HMAC key: invalid base64 in " + envHMACKey + ": " + err.Error())
}
if len(k) != 32 {
panic("checkpoint HMAC key: " + envHMACKey + " must decode to exactly 32 bytes, got " + fmt.Sprintf("%d", len(k)))
}
return k
}
k := make([]byte, 32)
if _, err := rand.Read(k); err != nil {
panic("failed to generate checkpoint HMAC key: " + err.Error())
}
common.Warn("checkpoint HMAC env not set — using random per-process key; checkpoint resume across restarts will fail", zap.String("env", envHMACKey))
return k
}
func computeCheckpointHMAC(payload []byte) []byte {
mac := hmac.New(sha256.New, checkpointHMACKey)
mac.Write(payload)
return mac.Sum(nil)
}
func loadCheckpoint(store CheckPointStore, ctx context.Context, cid string) (context.Context, *runContext, *ResumeInfo, error) {View on GitHub (pinned to 554fb1133a)
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
Example fix
# before export CHECKPOINT_HMAC_KEY="$(openssl rand -base64 16)" # panic: must decode to exactly 32 bytes, got 16 # after export CHECKPOINT_HMAC_KEY="$(openssl rand -base64 32)"
Defensive patterns
Strategy: validation
Validate before calling
k, err := base64.StdEncoding.DecodeString(strings.TrimSpace(v))
if err != nil || len(k) != 32 {
return fmt.Errorf("CHECKPOINT_HMAC_KEY must be base64 of exactly 32 bytes")
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- checkpoint HMAC key: invalid base64 in CHECKPOINT_HMAC_KEY:
- failed to initialize logger: {err}
- Invoke: invalid proxy URL %q: %v
- Invoke: proxy URL %q has no host
- ListOperations: nth requires n to be within the valid range
AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15).
Data as JSON: /api/errors/6856e612cdb50432.
Report an issue: GitHub.