juicedata/juicefs · error
failed to marshal checkpoint: %w
Error message
failed to marshal checkpoint: %w
What it means
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.
Source
Thrown at pkg/sync/checkpoint.go:300
for _, state := range ckpt.PrefixState {
state.RLock()
}
m.multipartUploadStore.RLock()
srcDelayDelMu.Lock()
ckpt.SrcDelayDel = append([]string(nil), srcDelayDel...)
srcDelayDelMu.Unlock()
dstDelayDelMu.Lock()
ckpt.DstDelayDel = append([]string(nil), dstDelayDel...)
dstDelayDelMu.Unlock()
data, err := json.Marshal(ckpt)
m.multipartUploadStore.RUnlock()
for _, state := range ckpt.PrefixState {
state.RUnlock()
}
ckpt.Unlock()
if err != nil {
return fmt.Errorf("failed to marshal checkpoint: %w", err)
}
logger.Debugf("Saving checkpoint with %d prefixes, copied: %d, failed: %d",
prefixCount, ckpt.Stats.Copied, ckpt.Stats.Failed)
reader := bytes.NewReader(data)
if err := m.dst.Put(ctx, m.checkpointKey, reader); err != nil {
return fmt.Errorf("failed to put checkpoint: %w", err)
}
return nil
}
// ValidateConfig checks if checkpoint config matches current config
func (m *CheckpointManager) ValidateConfig(current *Config) bool {
old := m.checkpoint.Config
// Must match fields
if old.Start != current.Start || old.End != current.End {View on GitHub (pinned to c9a67b23e8)
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
Example fix
// before
Copied float64 // may hold NaN
// after: guard before saving
if math.IsNaN(ckpt.Stats.Copied) {
ckpt.Stats.Copied = 0
}
data, err := json.Marshal(ckpt) Defensive patterns
Strategy: try-catch
Try / catch
if err := mgr.Save(ctx); err != nil {
if strings.Contains(err.Error(), "failed to marshal checkpoint") {
logger.Errorf("checkpoint not persisted: %v", err) // inspect field named in the error
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- failed to marshal segment message %s : %w
- failed to unmarshal checkpoint: %w
- marshal worker config: %s
- Cannot serialise open write-mode local file
- failed to marshal segment %s: %v
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/e801b3f31c9a89de.
Report an issue: GitHub.