{"record":{"id":"a7a13faa9b260673","repo":"cayleygraph/cayley","slug":"cannot-count-iterator-without-a-valid-context","errorCode":null,"errorMessage":"cannot count iterator without a valid context","messagePattern":"cannot count iterator without a valid context","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graph/gaedatastore/quadstore.go","lineNumber":539,"sourceCode":"\t}\n\treturn graph.Stats{\n\t\tNodes: refs.Size{\n\t\t\tValue: m.NodeCount,\n\t\t\tExact: true,\n\t\t},\n\t\tQuads: refs.Size{\n\t\t\tValue: m.QuadCount,\n\t\t\tExact: true,\n\t\t},\n\t}, nil\n}\n\nfunc (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, val graph.Ref) (refs.Size, error) {\n\tt, ok := val.(*Token)\n\tif !ok || t.Kind != nodeKind {\n\t\treturn refs.Size{Value: 0, Exact: true}, nil\n\t} else if qs.context == nil {\n\t\treturn refs.Size{}, errors.New(\"cannot count iterator without a valid context\")\n\t}\n\tkey := qs.createKeyFromToken(t)\n\tn := new(NodeEntry)\n\terr := datastore.Get(qs.context, key, n)\n\tif err != nil && err != datastore.ErrNoSuchEntity {\n\t\treturn refs.Size{}, err\n\t}\n\treturn refs.Size{Value: n.Size, Exact: true}, nil\n}\n\nfunc (qs *QuadStore) Close() error {\n\tqs.context = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) QuadDirection(val graph.Ref, dir quad.Direction) (graph.Ref, error) {\n\tt, ok := val.(*Token)\n\tif !ok {","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/cayleygraph/cayley/blob/81dcd7d73e45136bc0d01802a8ba4685d8a533eb/graph/gaedatastore/quadstore.go#L521-L557","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nqs := &gaedatastore.QuadStore{}\nsize, err := qs.QuadIteratorSize(ctx, quad.Any, nodeToken)\n// after\nqs, err := gaedatastore.New(ctx, opts) // ensures internal context is set\nif err != nil { return err }\nsize, err := qs.QuadIteratorSize(ctx, quad.Any, nodeToken)","handlingStrategy":"try-catch","validationCode":"if val, ok := ref.(*gaedatastore.Token); !ok || val.Kind != gaedatastore.NodeKind || qs.Context == nil {\n    // skip size query\n}","typeGuard":"func isCountableNodeToken(qs *gaedatastore.QuadStore, ref graph.Ref) bool {\n    t, ok := ref.(*gaedatastore.Token)\n    return ok && t.Kind == gaedatastore.NodeKind && qs.HasContext()\n}","tryCatchPattern":"size, err := qs.QuadIteratorSize(ctx, d, val)\nif err != nil {\n    if strings.Contains(err.Error(), \"valid context\") {\n        return refs.Size{}, fmt.Errorf(\"store not opened with datastore context: %w\", err)\n    }\n    return refs.Size{}, err\n}","preventionTips":["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."],"tags":["go","google-app-engine","datastore","context"],"backgroundTag":"missing-required-argument","analyzedSha":"81dcd7d73e45136bc0d01802a8ba4685d8a533eb","analyzedAt":"2026-09-06T06:14:12.358Z","contentChangedAt":"2026-09-06T06:14:12.358Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}