dgraph-io/dgraph · critical

could not read list part with key %s

Error message

could not read list part with key %s

What it means

This error wraps a lower-level badger failure that occurred while reading one part (chunk) of a multi-part posting list stored in Dgraph. Posting lists larger than the configured value capacity are split into parts keyed by the list key plus a suffix; reading a part that should exist failed. The wrap adds the hex-encoded part key so the specific unreadable chunk can be located.

Source

Thrown at posting/list.go:2274

		return nil, errors.Wrapf(err, "cannot retrieve facet")
	}
	fcs = append(fcs, &pb.Facets{Facets: facets.CopyFacets(p.Facets, param)})
	return fcs, nil
}

// readListPart reads one split of a posting list from Badger.
func (l *List) readListPart(startUid uint64) (*pb.PostingList, error) {
	key, err := x.SplitKey(l.key, startUid)
	if err != nil {
		return nil, errors.Wrapf(err,
			"cannot generate key for list with base key %s and start UID %d",
			hex.EncodeToString(l.key), startUid)
	}
	txn := pstore.NewTransactionAt(l.minTs, false)
	defer txn.Discard()
	item, err := txn.Get(key)
	if err != nil {
		return nil, errors.Wrapf(err, "could not read list part with key %s",
			hex.EncodeToString(key))
	}
	part := &pb.PostingList{}
	if err := unmarshalOrCopy(part, item); err != nil {
		return nil, errors.Wrapf(err, "cannot unmarshal list part with key %s",
			hex.EncodeToString(key))
	}
	return part, nil
}

// shouldSplit returns true if the given plist should be split in two.
func shouldSplit(plist *pb.PostingList) bool {
	return proto.Size(plist) >= maxListSize && len(plist.Pack.Blocks) > 1
}

func (out *rollupOutput) updateSplits() {
	if out.plist == nil || len(out.parts) > 0 {
		out.plist = &pb.PostingList{}

View on GitHub (pinned to 759e242be6)

Solutions

  1. Run badger integrity checks and restore from backup if the value log is corrupt
  2. Lower value-log GC aggressiveness / ensure DiscardTs is not set past needed versions
  3. Retry after rollup rebuilds the posting list (trigger a rollup over the affected key)
  4. Check disk health and badger logs for the underlying error cause

Example fix

// before
item, err := txn.Get(key)
if err != nil {
    return nil, errors.Wrapf(err, "could not read list part with key %s", hex.EncodeToString(key))
}
// after: log the underlying error and key, then retry via rollup or fail loudly
item, err := txn.Get(key)
if err != nil {
    if errors.Is(err, badger.ErrKeyNotFound) {
        return nil, errors.Wrapf(err, "list part missing (possibly GC'd or rollup pending) key %s", hex.EncodeToString(key))
    }
    return nil, errors.Wrapf(err, "could not read list part with key %s", hex.EncodeToString(key))
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check: read the list at a recent readTs and confirm the part exists
func partExists(kv *badger.DB, key []byte, readTs uint64) bool {
    txn := kv.NewTransactionAt(readTs, false)
    defer txn.Discard()
    _, err := txn.Get(key)
    return err == nil
}

Try / catch

// Retry read after rollup; surface underlying error
part, err := readListPart(key, startUid)
if err != nil {
    if isRetryable(err) { // badger I/O or transient ts issues
        triggerRollup(key)
        part, err = readListPart(key, startUid)
    }
    if err != nil { return fmt.Errorf("list part %x unreadable: %w", key, err) }
}

Prevention

When it happens

Trigger: Calling readListPart (via List iteration, rollup, or query evaluation) when txn.Get(key) fails inside a transaction pinned at l.minTs — e.g. the part was garbage-collected, the key was never written, or the underlying badger read returned an I/O/corruption error.

Common situations: Badger DB corruption or missing value-log files after an unclean shutdown; reading at a timestamp older than retained versions (too-aggressive GC); manual deletion of badger files; disk errors.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/2c2a69eb3504498b. Report an issue: GitHub.