cayleygraph/cayley · error
cannot count iterator without a valid context
Error message
cannot count iterator without a valid context
What it means
QuadIteratorSize counts how many quads reference a given node value. The GAED datastore stores the node-entry lookup in qs.context (the datastore client/transaction context), so if the store was constructed without a valid context there is no datastore handle to issue the Get against, and the method fails with this error instead of panicking on a nil context.
Source
Thrown at graph/gaedatastore/quadstore.go:539
}
return graph.Stats{
Nodes: refs.Size{
Value: m.NodeCount,
Exact: true,
},
Quads: refs.Size{
Value: m.QuadCount,
Exact: true,
},
}, nil
}
func (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {
t, ok := val.(*Token)
if !ok || t.Kind != nodeKind {
return refs.Size{Value: 0, Exact: true}, nil
} else if qs.context == nil {
return refs.Size{}, errors.New("cannot count iterator without a valid context")
}
key := qs.createKeyFromToken(t)
n := new(NodeEntry)
err := datastore.Get(qs.context, key, n)
if err != nil && err != datastore.ErrNoSuchEntity {
return refs.Size{}, err
}
return refs.Size{Value: n.Size, Exact: true}, nil
}
func (qs *QuadStore) Close() error {
qs.context = nil
return nil
}
func (qs *QuadStore) QuadDirection(val graph.Ref, dir quad.Direction) (graph.Ref, error) {
t, ok := val.(*Token)
if !ok {View on GitHub (pinned to 81dcd7d73e)
Solutions
- Initialize the QuadStore through its normal open/new path so qs.context is set to a valid datastore client/transaction before calling QuadIteratorSize.
- If constructing the store manually, assign the App Engine datastore context to qs.context prior to any query.
- Guard the call: check that the store was opened successfully (no nil context) before counting iterator sizes.
Example fix
// before
qs := &gaedatastore.QuadStore{}
size, err := qs.QuadIteratorSize(ctx, quad.Any, nodeToken)
// after
qs, err := gaedatastore.New(ctx, opts) // ensures internal context is set
if err != nil { return err }
size, err := qs.QuadIteratorSize(ctx, quad.Any, nodeToken) Defensive patterns
Strategy: try-catch
Validate before calling
if val, ok := ref.(*gaedatastore.Token); !ok || val.Kind != gaedatastore.NodeKind || qs.Context == nil {
// skip size query
} Type guard
func isCountableNodeToken(qs *gaedatastore.QuadStore, ref graph.Ref) bool {
t, ok := ref.(*gaedatastore.Token)
return ok && t.Kind == gaedatastore.NodeKind && qs.HasContext()
} Try / catch
size, err := qs.QuadIteratorSize(ctx, d, val)
if err != nil {
if strings.Contains(err.Error(), "valid context") {
return refs.Size{}, fmt.Errorf("store not opened with datastore context: %w", err)
}
return refs.Size{}, err
} Prevention
- Always construct the gaedatastore QuadStore through its supported open/new constructor.
- Never instantiate QuadStore{} literals in application code.
- In tests, inject a valid App Engine datastore context before querying.
When it happens
Trigger: Calling QuadIteratorSize(ctx, d, val) where val is a *Token with Kind == nodeKind (i.e. a node value) while the QuadStore's qs.context field is nil. The error is only reachable for node tokens; non-node tokens return a zero size early.
Common situations: Embedding or manually constructing gaedatastore.QuadStore (e.g. in tests or custom deployments) without running the normal open path that assigns the datastore context; running on App Engine with a context that was never injected into the store.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Datastore: invalid action
- Nothing to log
- token not valid
- gae quad: token not valid
- node tokens not valid
AI-assisted analysis of cayleygraph/cayley@81dcd7d73e (2026-09-06).
Data as JSON: /api/errors/a7a13faa9b260673.
Report an issue: GitHub.