{"record":{"id":"e801b3f31c9a89de","repo":"juicedata/juicefs","slug":"failed-to-marshal-checkpoint-w","errorCode":null,"errorMessage":"failed to marshal checkpoint: %w","messagePattern":"failed to marshal checkpoint: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/sync/checkpoint.go","lineNumber":300,"sourceCode":"\tfor _, state := range ckpt.PrefixState {\n\t\tstate.RLock()\n\t}\n\tm.multipartUploadStore.RLock()\n\tsrcDelayDelMu.Lock()\n\tckpt.SrcDelayDel = append([]string(nil), srcDelayDel...)\n\tsrcDelayDelMu.Unlock()\n\tdstDelayDelMu.Lock()\n\tckpt.DstDelayDel = append([]string(nil), dstDelayDel...)\n\tdstDelayDelMu.Unlock()\n\tdata, err := json.Marshal(ckpt)\n\tm.multipartUploadStore.RUnlock()\n\tfor _, state := range ckpt.PrefixState {\n\t\tstate.RUnlock()\n\t}\n\tckpt.Unlock()\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal checkpoint: %w\", err)\n\t}\n\n\tlogger.Debugf(\"Saving checkpoint with %d prefixes, copied: %d, failed: %d\",\n\t\tprefixCount, ckpt.Stats.Copied, ckpt.Stats.Failed)\n\treader := bytes.NewReader(data)\n\tif err := m.dst.Put(ctx, m.checkpointKey, reader); err != nil {\n\t\treturn fmt.Errorf(\"failed to put checkpoint: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// ValidateConfig checks if checkpoint config matches current config\nfunc (m *CheckpointManager) ValidateConfig(current *Config) bool {\n\told := m.checkpoint.Config\n\n\t// Must match fields\n\tif old.Start != current.Start || old.End != current.End {","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/sync/checkpoint.go#L282-L318","documentation":"Save() serializes the current checkpoint (after collecting prefix states under locks) with json.Marshal. This error wraps a json.Marshal failure, meaning some field of the Checkpoint cannot be represented in JSON (e.g. unsupported type such as a channel/func, invalid number like NaN, or a custom MarshalJSON error). Save aborts and the checkpoint is not persisted, so progress since the last successful save would be lost on interruption.","triggerScenarios":"Periodic checkpoint save (or explicit Save from tests/seedCheckpoint) executes json.Marshal(ckpt) and it returns an error — typically due to a non-JSON-serializable value added to Checkpoint/PrefixState fields, e.g. NaN/Inf floats in Stats or an unexpected field type introduced in a code change.","commonSituations":"Developers extending Checkpoint with a new field of a non-marshalable type; counters corrupted to NaN; concurrent mutation causing inconsistent state passed to the encoder.","solutions":["Inspect the wrapped error to find the offending field; make it JSON-serializable (use plain types, fix NaN/Inf values)","Check recent changes to the Checkpoint/multipartUploadState/PrefixState structs for non-serializable fields","Add a marshal step test (TestCheckpointManagerSaveAndLoad covers round-trip) and fix the field type"],"exampleFix":"// before\nCopied float64 // may hold NaN\n// after: guard before saving\nif math.IsNaN(ckpt.Stats.Copied) {\n\tckpt.Stats.Copied = 0\n}\ndata, err := json.Marshal(ckpt)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := mgr.Save(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"failed to marshal checkpoint\") {\n\t\tlogger.Errorf(\"checkpoint not persisted: %v\", err) // inspect field named in the error\n\t}\n}","preventionTips":["Keep Checkpoint struct fields JSON-serializable (no chan/func; guard NaN/Inf floats)","Round-trip test (Save then Load) whenever the Checkpoint schema changes","Avoid concurrent mutation of Stats without the manager's locks"],"tags":["json","checkpoint","marshal","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}