{"record":{"id":"6f18b5ff42923107","repo":"cayleygraph/cayley","slug":"datastore-invalid-action","errorCode":null,"errorMessage":"Datastore: invalid action","messagePattern":"Datastore: invalid action","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graph/gaedatastore/quadstore.go","lineNumber":216,"sourceCode":"\t\treturn 0, err\n\t}\n\treturn len(buf), nil\n}\n\nfunc (w *quadWriter) Close() error {\n\tw.deltas = nil\n\treturn nil\n}\n\nfunc (qs *QuadStore) ApplyDeltas(in []graph.Delta, ignoreOpts graph.IgnoreOpts) error {\n\tif qs.context == nil {\n\t\treturn errors.New(\"No context, graph not correctly initialised\")\n\t}\n\ttoKeep := make([]graph.Delta, 0)\n\tfor _, d := range in {\n\t\tif d.Action != graph.Add && d.Action != graph.Delete {\n\t\t\t//Defensive shortcut\n\t\t\treturn errors.New(\"Datastore: invalid action\")\n\t\t}\n\t\tkey := qs.createKeyForQuad(d.Quad)\n\t\tkeep := false\n\t\tswitch d.Action {\n\t\tcase graph.Add:\n\t\t\tfound, err := qs.checkValid(key)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif found {\n\t\t\t\tif !ignoreOpts.IgnoreDup {\n\t\t\t\t\treturn graph.ErrQuadExists\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tkeep = true\n\t\t\t}\n\t\tcase graph.Delete:\n\t\t\tfound, err := qs.checkValid(key)","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/cayleygraph/cayley/blob/81dcd7d73e45136bc0d01802a8ba4685d8a533eb/graph/gaedatastore/quadstore.go#L198-L234","documentation":"ApplyDeltas validates every delta's Action before applying it; only graph.Add and graph.Delete are supported. Any other action value causes an immediate abort of the whole batch — no partial application.","triggerScenarios":"Passing a []graph.Delta to ApplyDeltas (or the wrappers WriteQuads/AddQuadSet/ApplyTransaction) where any delta has an Action other than graph.Add or graph.Delete (e.g. zero value, graph.Update, or corrupt delta).","commonSituations":"Constructing deltas manually and forgetting to set Action; building a Delta with a struct literal that omits the Action field so it defaults to zero; middleware that produces custom action codes.","solutions":["Audit delta construction and set Action explicitly to graph.Add or graph.Delete for every delta.","Validate the batch before calling ApplyDeltas and reject unknown actions early.","Update code that uses an action value this backend does not support; the GAE datastore backend only implements Add/Delete.","Check delta-producing helpers for paths that return an empty/zero Delta."],"exampleFix":"// before\ndeltas = append(deltas, graph.Delta{Quad: q})\n// after\ndeltas = append(deltas, graph.Delta{Quad: q, Action: graph.Add})","handlingStrategy":"validation","validationCode":"for i, d := range deltas {\n    if d.Action != graph.Add && d.Action != graph.Delete {\n        return fmt.Errorf(\"delta %d has invalid action %v\", i, d.Action)\n    }\n}","typeGuard":"func validActions(ds []graph.Delta) bool {\n    for _, d := range ds {\n        if d.Action != graph.Add && d.Action != graph.Delete { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := qs.ApplyDeltas(deltas, ignoreOpts); err != nil {\n    if strings.Contains(err.Error(), \"invalid action\") {\n        // log offending batch, fix producer\n    }\n    return err\n}","preventionTips":["Use constructor helpers for deltas that always set Action.","Never rely on zero values for enum fields.","Unit-test delta producers to assert valid actions.","Validate batches at the boundary before store calls."],"tags":["datastore","validation","deltas","invalid-argument"],"backgroundTag":"invalid-enum-value","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"}