juicedata/juicefs · error
segment %s is nil
Error message
segment %s is nil
What it means
BakSegment.Marshal refuses to serialize a segment when either the BakSegment pointer is nil or its val (the embedded proto.Message) is nil. This is an internal invariant check during backup writing: writeFooter/the segment writer should never hand Marshal a segment without a payload.
Source
Thrown at pkg/meta/backup.go:327
return uint64(len(b.Xattrs))
case segTypeAcl:
return uint64(len(b.Acls))
case segTypeStat:
return uint64(len(b.Dirstats))
case segTypeQuota:
return uint64(len(b.Quotas) + len(b.UserQuotas) + len(b.GroupQuotas))
case segTypeParent:
return uint64(len(b.Parents))
case segTypeChangeLog:
return uint64(len(b.Changelogs))
}
return 0
}
}
func (s *BakSegment) Marshal(w io.Writer) (int, error) {
if s == nil || s.val == nil {
return 0, fmt.Errorf("segment %s is nil", s)
}
if err := binary.Write(w, binary.BigEndian, s.typ); err != nil {
return 0, fmt.Errorf("failed to write segment type %s : %w", s, err)
}
data, err := proto.Marshal(s.val)
if err != nil {
return 0, fmt.Errorf("failed to marshal segment message %s : %w", s, err)
}
s.len = uint64(len(data))
if err := binary.Write(w, binary.BigEndian, s.len); err != nil {
return 0, fmt.Errorf("failed to write segment length %s: %w", s, err)
}
if n, err := w.Write(data); err != nil || n != len(data) {
return 0, fmt.Errorf("failed to write segment data %s: err %w, write len %d, expect len %d", s, err, n, len(data))
}
View on GitHub (pinned to c9a67b23e8)
Solutions
- Initialize segments via newBakSegment(val) with a non-nil proto.Message instead of constructing BakSegment literals.
- Check the segment slice before writing: skip or error on nil segments rather than attempting to marshal them.
- If newBakSegment returned nil (a pb.Batch with no recognized field set), do not append it — that nil return means 'nothing to write'.
- Ensure val is populated before Marshal is invoked (assign the *pb.Format or *pb.Batch before queuing the segment).
Example fix
// before: appending possibly-nil segment
seg := newBakSegment(batch)
segments = append(segments, seg)
// after: skip nil segments
seg := newBakSegment(batch)
if seg == nil {
return nil // empty batch: nothing to serialize
}
segments = append(segments, seg) Defensive patterns
Strategy: validation
Validate before calling
if seg == nil || seg.val == nil {
return errors.New("refusing to marshal nil segment")
} Type guard
func validSegment(s *BakSegment) bool { return s != nil && s.val != nil } Try / catch
n, err := seg.Marshal(w)
if err != nil {
if strings.Contains(err.Error(), "is nil") {
return fmt.Errorf("programming bug: nil segment queued for backup: %w", err)
}
return err
} Prevention
- Always construct segments through newBakSegment, never as raw literals.
- Treat newBakSegment's nil return (empty batch) as 'skip', never append it.
- Filter nil entries from the segment list before the write loop.
- Assign val before enqueueing a segment for writing.
When it happens
Trigger: Passing nil to Marshal directly; constructing a segment whose proto.Message was never set; a caller that built a BakSegment but skipped newBakSegment initialization; code paths that append a segment before populating val.
Common situations: Custom tooling built on the internal BakFormat API writing backup files; a bug where a nil batch was appended after filtering produced no content; reusing a BakSegment value after it was reset; reflection-based code creating a BakSegment without initializing fields.
Related errors
- unknown message type %d
- failed to find message %s's type: %v
- failed to marshal segment %s: %v
- failed to marshal footer: %w
- failed to read footer: err %w, read len %d, expect len %d
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/aa5a8c8dc82b9c88.
Report an issue: GitHub.