{"record":{"id":"523921d50d196da2","repo":"d2lang/d2","slug":"moving-across-scopes-isn-t-supported-for-edges","errorCode":null,"errorMessage":"moving across scopes isn't supported for edges","messagePattern":"moving across scopes isn't supported for edges","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2oracle/edit.go","lineNumber":1808,"sourceCode":"\t\treturn nil, err\n\t}\n\n\tmk, err := d2parser.ParseMapKey(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmk2, err := d2parser.ParseMapKey(newKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tedgeTrimCommon(mk)\n\tedgeTrimCommon(mk2)\n\n\tif len(mk.Edges) > 0 && mk.EdgeKey == nil {\n\t\tif d2format.Format(mk.Key) != d2format.Format(mk2.Key) {\n\t\t\t// TODO just prevent moving edges at all\n\t\t\treturn nil, errors.New(\"moving across scopes isn't supported for edges\")\n\t\t}\n\t\tobj := g.Root\n\t\tif mk.Key != nil {\n\t\t\tvar ok bool\n\t\t\tobj, ok = g.Root.HasChild(d2graph.Key(mk.Key))\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"edge referenced by from does not exist\")\n\t\t\t}\n\t\t}\n\t\te, ok := obj.HasEdge(mk)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"edge referenced by to does not exist\")\n\t\t}\n\t\t_, ok = obj.HasEdge(mk2)\n\t\tif ok {\n\t\t\treturn nil, fmt.Errorf(\"to edge already exists\")\n\t\t}\n","sourceCodeStart":1790,"sourceCodeEnd":1826,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2oracle/edit.go#L1790-L1826","documentation":"This error is thrown by d2oracle's move function when asked to move an edge (a connection like 'a -> b') to a different scope. Edge keys encode both endpoints and scope, so relocating an edge to a differently-scoped map key (where the formatted key of the move differs from the target key) is not a supported edit. The library intentionally rejects the operation instead of producing a corrupt rewrite.","triggerScenarios":"Calling d2oracle.Move, d2oracle.Rename, or renameConflictsToParent with mk.Edges non-empty and mk.EdgeKey == nil, where d2format.Format(mk.Key) differs from d2format.Format(mk2.Key) — i.e. the edge's map key path changes across scopes.","commonSituations":"Programmatic graph refactors that try to drag an edge into or out of a nested container/shape; renaming a parent shape while moving its edge to another scope in the same edit; tools built on d2oracle that assume edges move like nodes.","solutions":["Move the edge's endpoints (nodes) instead of the edge itself; the edge stays defined in its original scope.","Recreate the edge in the target scope: Delete the old edge and Create a new one with the desired key/endpoints.","Keep the edge's map key identical across the move (same formatted Key) so the move is within-scope.","Patch your tooling to detect edges in mk.Edges before calling Move and route them to a delete+create flow."],"exampleFix":"// before\ndelta, err := d2oracle.Move(graph.Id, \"x.y -> x.z\", \"x.a.y -> x.a.z\") // cross-scope edge move: error\n// after\n// move the endpoints, not the edge:\ndelta, err := d2oracle.Move(graph.Id, \"x.y\", \"x.a.y\")\ndelta2, err2 := d2oracle.Move(graph.Id, \"x.z\", \"x.a.z\")","handlingStrategy":"validation","validationCode":"mk, err := d2parser.ParseMapKey(fromKey)\nif err != nil { return err }\nmk2, err := d2parser.ParseMapKey(toKey)\nif err != nil { return err }\nif len(mk.Edges) > 0 && mk.EdgeKey == nil && d2format.Format(mk.Key) != d2format.Format(mk2.Key) {\n    return fmt.Errorf(\"edge %q cannot be moved across scopes; delete+create instead\", fromKey)\n}","typeGuard":"func isEdgeWithinScope(mk, mk2 *d2parser.MapKey) bool {\n    return len(mk.Edges) == 0 || mk.EdgeKey != nil || d2format.Format(mk.Key) == d2format.Format(mk2.Key)\n}","tryCatchPattern":"delta, err := d2oracle.Move(id, fromKey, toKey)\nif err != nil {\n    if err.Error() == \"moving across scopes isn't supported for edges\" {\n        return deleteAndRecreateEdge(g, fromKey, toKey)\n    }\n    return err\n}","preventionTips":["Check len(mk.Edges) before calling Move/Rename on a key","Model edge relocation as delete+create in tooling","Keep edges in the scope where their endpoints are defined","Route node moves and edge edits through separate code paths"],"tags":["d2oracle","edges","unsupported-operation","graph-edit"],"backgroundTag":"unsupported-edge-move","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}