{"record":{"id":"a12131cf66ee41d4","repo":"d2lang/d2","slug":"cannot-rename-to-reserved-keyword-v","errorCode":null,"errorMessage":"cannot rename to reserved keyword: %#v","messagePattern":"cannot rename to reserved keyword: %#v","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2oracle/edit.go","lineNumber":1731,"sourceCode":"\t\tif boardG == nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"board %v not found\", boardPath)\n\t\t}\n\t}\n\n\tif len(mk.Edges) > 0 && mk.EdgeKey == nil {\n\t\t// TODO: Not a fan of this dual interpretation depending on mk.Edges.\n\t\t// Maybe we remove Rename and just have Move.\n\t\tmk2, err := d2parser.ParseMapKey(newName)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", err\n\t\t}\n\n\t\tmk2.Key = mk.Key\n\t\tmk = mk2\n\t} else {\n\t\t_, ok := d2ast.ReservedKeywords[newName]\n\t\tif ok {\n\t\t\treturn nil, \"\", fmt.Errorf(\"cannot rename to reserved keyword: %#v\", newName)\n\t\t}\n\t\tif mk.Key != nil {\n\t\t\tobj, ok := boardG.Root.HasChild(d2graph.Key(mk.Key))\n\t\t\tif !ok {\n\t\t\t\treturn nil, \"\", fmt.Errorf(\"key does not exist\")\n\t\t\t}\n\t\t\t// If attempt to name something \"x\", but \"x\" already exists, rename it \"x 2\" instead\n\t\t\tgeneratedName, _, err := generateUniqueKey(boardG, newName, obj, nil)\n\t\t\tif err == nil {\n\t\t\t\tnewName = generatedName\n\t\t\t}\n\t\t}\n\t\t// TODO: Handle mk.EdgeKey\n\t\tmk.Key.Path[len(mk.Key.Path)-1] = d2ast.MakeValueBox(d2ast.RawString(newName, true)).StringBox()\n\t}\n\n\tg, err = move(g, boardPath, key, d2format.Format(mk), false)\n\treturn g, newName, err","sourceCodeStart":1713,"sourceCodeEnd":1749,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2oracle/edit.go#L1713-L1749","documentation":"Rename rejects newName values that collide with d2 reserved keywords (like shape, style, label, etc.) because those identifiers cannot be used as regular object keys; the library returns \"cannot rename to reserved keyword\" instead of producing a broken diagram.","triggerScenarios":"Calling Rename with newName equal to any key in d2ast.ReservedKeywords, e.g. renaming an object to \"shape\" or \"style\" when it has no edge interpretation.","commonSituations":"Renaming based on raw user input in editors; scripted refactors that don't sanitize names; autocompletion suggesting reserved words.","solutions":["Validate newName against d2ast.ReservedKeywords before calling Rename.","Sanitize/escape user-supplied names (prefix, quote, or reject reserved words in UI).","Pick a different name for the object.","If a reserved name is genuinely needed, wrap it appropriately in the source rather than via Rename."],"exampleFix":"// before\nd2oracle.Rename(g, nil, \"db\", \"style\") // rejected\n// after\nif _, reserved := d2ast.ReservedKeywords[newName]; reserved {\n    newName = newName + \"-node\"\n}\nd2oracle.Rename(g, nil, \"db\", newName)","handlingStrategy":"validation","validationCode":"import \"oss.terrastruct.com/d2/lib/d2ast\"\nif _, reserved := d2ast.ReservedKeywords[newName]; reserved {\n    return fmt.Errorf(\"%q is a reserved d2 keyword; pick another name\", newName)\n}","typeGuard":"func isAllowedName(name string) bool {\n    _, reserved := d2ast.ReservedKeywords[name]\n    return name != \"\" && !reserved\n}","tryCatchPattern":"ng, _, err := d2oracle.Rename(g, boardPath, key, newName)\nif err != nil && strings.Contains(err.Error(), \"reserved keyword\") {\n    return suggestAlternativeName(newName)\n}","preventionTips":["Validate user input against d2ast.ReservedKeywords before calling.","Sanitize names in UI (block reserved words in autocomplete).","Add a suffix/quote strategy for reserved names in refactors.","Test rename flows with reserved words in CI."],"tags":["d2","rename","reserved-keywords","validation"],"backgroundTag":"reserved-keyword-name","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}