cayleygraph/cayley · error

cannot get horizon value: %v

Error message

cannot get horizon value: %v

What it means

getMetaIntTx fetches a 8-byte little-endian integer from the meta bucket within a transaction. ErrNotFound is normal (treated as 0), but any other storage error is wrapped as "cannot get horizon value". The name references the horizon/size counters kept in the meta bucket, despite the generic key parameter.

Source

Thrown at graph/kv/indexing.go:240

			continue
		}
		ind := inds[i]
		id, _ := binary.Uvarint(b)
		d := &deltas[ind]
		if iri, ok := d.Val.(quad.IRI); ok && id != 0 {
			qs.valueLRU.Put(string(iri), uint64(id))
		}
		fnc(ind, uint64(id))
	}
	return nil
}

func (qs *QuadStore) getMetaIntTx(ctx context.Context, tx kv.Tx, key string) (int64, error) {
	val, err := tx.Get(ctx, metaBucket.AppendBytes([]byte(key)))
	if err == kv.ErrNotFound {
		return 0, err
	} else if err != nil {
		return 0, fmt.Errorf("cannot get horizon value: %v", err)
	}
	return int64(binary.LittleEndian.Uint64(val)), nil
}

func (qs *QuadStore) incMetaInt(ctx context.Context, tx kv.Tx, key string, n int64) (int64, error) {
	if n == 0 {
		return 0, nil
	}
	v, err := qs.getMetaIntTx(ctx, tx, key)
	if err != nil && err != kv.ErrNotFound {
		return 0, fmt.Errorf("cannot get %s: %v", key, err)
	}
	start := v
	v += n

	buf := make([]byte, 8) // bolt needs all slices available on Commit
	binary.LittleEndian.PutUint64(buf, uint64(v))

View on GitHub (pinned to 81dcd7d73e)

Solutions

  1. Inspect the wrapped inner error (%v) to identify the backend failure and fix that root cause.
  2. Verify the database file is accessible and not locked by another process or opened with conflicting options.
  3. Check disk health/space and file permissions on the KV database.
  4. Restart the process and reopen the store; if corruption persists, restore from backup and reindex.

Example fix

// before: store closed while goroutines still write
qs.Close(); qs.incSize(ctx, 1) // cannot get horizon value: tx on closed db
// after
// ensure all writers finish before Close, or reopen the store before writing
qs2, err := kv.New(...); qs2.incSize(ctx, 1)
Defensive patterns

Strategy: retry

Validate before calling

if qs == nil || closed {
    return fmt.Errorf("store must be open before meta operations")
}

Try / catch

n, err := qs.incSize(ctx, 1)
if err != nil {
    if errors.Is(err, kv.ErrNotFound) {
        n = 0 // first increment
    } else if strings.Contains(err.Error(), "cannot get") {
        // inspect wrapped backend error, reopen store or fail fast
        return reopenAndRetry()
    }
}

Prevention

When it happens

Trigger: Any kv transaction Get on a meta key failing with a storage-level error (I/O error, closed DB, bolt/leveldb failure) during incMetaInt-driven writes such as size increments or ID generation.

Common situations: Underlying KV database file locked or corrupted; disk I/O errors; transaction used after the store was closed; backend-specific failures surfaced through kv.Tx.Get.

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 cayleygraph/cayley@81dcd7d73e (2026-09-06). Data as JSON: /api/errors/6b81b084560afc0e. Report an issue: GitHub.