{"record":{"id":"dc72668b9421507e","repo":"cayleygraph/cayley","slug":"must-specify-a-tag-name-when-saving-a-path","errorCode":null,"errorMessage":"must specify a tag name when saving a path","messagePattern":"must specify a tag name when saving a path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"query/gizmo/traversals.go","lineNumber":468,"sourceCode":"}\nfunc (p *pathObject) save(call goja.FunctionCall, rev, opt bool) goja.Value {\n\targs := exportArgs(call.Arguments)\n\tif len(args) > 2 || len(args) == 0 {\n\t\treturn throwErr(p.s.vm, errArgCount{Got: len(args)})\n\t}\n\tvar vtag interface{} = \"\"\n\tif len(args) == 2 {\n\t\tvtag = args[1]\n\t}\n\ttag, ok := vtag.(string)\n\tif !ok {\n\t\treturn throwErr(p.s.vm, fmt.Errorf(\"expected string, got: %T\", vtag))\n\t}\n\tvia := args[0]\n\tif vp, ok := via.(*pathObject); ok {\n\t\tvia = vp.path\n\t\tif tag == \"\" {\n\t\t\treturn throwErr(p.s.vm, errors.New(\"must specify a tag name when saving a path\"))\n\t\t}\n\t} else {\n\t\tqv, err := toQuadValue(via)\n\t\tvia = qv\n\t\tif err != nil {\n\t\t\treturn throwErr(p.s.vm, err)\n\t\t}\n\t\tif tag == \"\" {\n\t\t\tif p.s.col == query.JSONLD {\n\t\t\t\tswitch qv := qv.(type) {\n\t\t\t\tcase quad.IRI:\n\t\t\t\t\ttag = string(qv)\n\t\t\t\tcase quad.String:\n\t\t\t\t\ttag = string(qv)\n\t\t\t\tdefault:\n\t\t\t\t\ttag = quad.StringOf(qv)\n\t\t\t\t}\n\t\t\t} else {","sourceCodeStart":450,"sourceCodeEnd":486,"githubUrl":"https://github.com/cayleygraph/cayley/blob/81dcd7d73e45136bc0d01802a8ba4685d8a533eb/query/gizmo/traversals.go#L450-L486","documentation":"In the Gizmo (Gremlin-like) query language, Save(via) stores the reached value under a tag. When the \"via\" argument is a path object (a subquery) rather than a plain value, the tag name becomes mandatory because it can no longer be inferred from a literal predicate/vertex string. If the tag string is empty in that case, the VM aborts with this error.","triggerScenarios":"Calling g.Save(<pathObject>, \"\") or SaveR/SaveOpt/SaveOptR with a path as the via argument and an empty tag string. E.g. g.Save(g.V(\"foo\").Out(), \"\") — a path via with no tag.","commonSituations":"Copy-pasting Save calls where the first form used a string predicate (tag optional) and later swapping in a path; building queries programmatically and leaving the tag empty; misunderstanding that a tag is optional only when via is a literal value.","solutions":["Provide a non-empty tag string as the second argument: g.Save(g.V(\"foo\").Out(), \"myTag\").","If you meant to save a literal predicate value, pass the string directly (tag can stay implicit for string via): g.Save(\"pred\", \"tag\").","Review SaveR/SaveOpt/SaveOptR call sites for an empty-string tag when via is a path.","Validate tag names when generating Gizmo queries from templates or code."],"exampleFix":"// before\nit := g.Save(g.V(\"status\").Out(\"followers\"), \"\")\n// after\nit := g.Save(g.V(\"status\").Out(\"followers\"), \"followersTag\")","handlingStrategy":"validation","validationCode":"function savePath(pathObj, tag) {\n  if (pathObj instanceof g.Path && (!tag || typeof tag !== \"string\")) {\n    throw new Error(\"Save with a path via requires a non-empty tag string\");\n  }\n  return g.Save(pathObj, tag);\n}","typeGuard":"function isPathObject(v) { return v != null && typeof v === \"object\" && typeof v.ToArray === \"function\"; }\n// if isPathObject(via) and tag === \"\" -> Save will fail; require a tag","tryCatchPattern":"try {\n  result = g.Save(via, tag).Iterate();\n} catch (e) {\n  if (e.message.includes(\"must specify a tag name\")) {\n    throw new Error(\"Save(path, tag) requires a non-empty tag; supply one for via=\" + via);\n  }\n  throw e;\n}","preventionTips":["Always pass an explicit tag when the via argument is a path (g.V().Out(), etc.).","Only omit/empty the tag when via is a literal string value.","Lint generated Gizmo queries for Save(..., \"\") patterns."],"tags":["query","gizmo","missing-argument","tags"],"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"}