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

  1. Initialize segments via newBakSegment(val) with a non-nil proto.Message instead of constructing BakSegment literals.
  2. Check the segment slice before writing: skip or error on nil segments rather than attempting to marshal them.
  3. If newBakSegment returned nil (a pb.Batch with no recognized field set), do not append it — that nil return means 'nothing to write'.
  4. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/aa5a8c8dc82b9c88. Report an issue: GitHub.