juicedata/juicefs · error

get range

Error message

get range [%v-%v): %s

What it means

`etcdTxn.scan` reads a half-open key range [begin, end) from etcd; if the range Get RPC fails the transaction panics with this message naming the range. Scans power directory listing and other range reads in the TKV metadata layer.

Solutions

  1. Run `etcdctl endpoint health` and check etcd logs for the failing range request
  2. Fix connectivity/auth (endpoints, certificates, user permissions on the key range)
  3. Retry the listing after transient etcd issues; increase client timeout for large directories
  4. Keep directories reasonably sized or shard keys if scans routinely time out
Defensive patterns

Strategy: retry

Validate before calling

etcdctl --endpoints=host:2379 endpoint health && etcdctl auth status

Try / catch

if err := listDir(); err != nil {
    if isTransient(err) { time.Sleep(backoff); listDir() } else { return err }
}

Prevention

When it happens

Trigger: Listing a directory or scanning metadata keys in an etcd-backed volume when the etcd range query errors: unreachable endpoint, auth revoked, context deadline exceeded, etcd compaction/leader change mid-request.

Common situations: etcd overloaded by large directory scans; network flakiness; expired etcd user credentials/token; request timeout on huge directories over slow links.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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

Appendix: source

Thrown at pkg/meta/tkv_etcd.go:117

			values[i] = v
			continue
		}
		values[i] = rs[k]
		if len(values[i]) == 0 {
			tx.observed[k] = 0
		}
	}
	return values
}

func (tx *etcdTxn) scan(begin, end []byte, keysOnly bool, handler func(k, v []byte) bool) {
	opts := []etcd.OpOption{etcd.WithRange(string(end))}
	if keysOnly {
		opts = append(opts, etcd.WithKeysOnly())
	}
	resp, err := tx.kv.Get(tx.ctx, string(begin), opts...)
	if err != nil {
		panic(fmt.Errorf("get range [%v-%v): %s", string(begin), string(end), err))
	}
	for _, kv := range resp.Kvs {
		tx.observed[string(kv.Key)] = kv.ModRevision
		if !handler(kv.Key, kv.Value) {
			break
		}
	}
}

func (tx *etcdTxn) exist(prefix []byte) bool {
	resp, err := tx.kv.Get(tx.ctx, string(prefix), etcd.WithPrefix(), etcd.WithCountOnly())
	if err != nil {
		panic(fmt.Errorf("get prefix %v with count only: %s", string(prefix), err))
	}
	return resp.Count > 0
}

func (tx *etcdTxn) set(key, value []byte) {

View on GitHub (pinned to c9a67b23e8)