{"record":{"id":"9abc635a09c404d6","repo":"d2lang/d2","slug":"board-v-ast-not-found","errorCode":null,"errorMessage":"board %v AST not found","messagePattern":"board (.+?) AST not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2oracle/edit.go","lineNumber":64,"sourceCode":"\t\t\treturn nil, \"\", fmt.Errorf(\"board %v cannot be modified through this file\", boardPath)\n\t\t}\n\t}\n\n\tnewKey, edge, err := generateUniqueKey(boardG, key, nil, nil)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tif edge {\n\t\terr = _set(boardG, baseAST, key, nil, nil)\n\t} else {\n\t\terr = _set(boardG, baseAST, newKey, nil, nil)\n\t}\n\n\tif len(boardPath) > 0 {\n\t\treplaced := ReplaceBoardNode(g.AST, baseAST, boardPath)\n\t\tif !replaced {\n\t\t\treturn nil, \"\", fmt.Errorf(\"board %v AST not found\", boardPath)\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tg, err = recompile(g)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\treturn g, newKey, nil\n}\n\n// TODO: update graph in place when compiler can accept single modifications\n// TODO: go through all references to decide best spot to insert something\nfunc Set(g *d2graph.Graph, boardPath []string, key string, tag, value *string) (_ *d2graph.Graph, err error) {\n\tvar valueHelp string\n\tif value == nil {","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2oracle/edit.go#L46-L82","documentation":"After Create mutates the board's BaseAST, it splices the edited AST back into the root file AST with ReplaceBoardNode(g.AST, baseAST, boardPath). If no node at boardPath in the root AST matches baseAST, the splice fails and Create returns 'board %v AST not found', meaning the in-memory board and the root AST are out of sync.","triggerScenarios":"Calling Create(g, boardPath, key) where GetBoardGraph found the board but ReplaceBoardNode cannot find a matching board node at boardPath in g.AST — e.g. boardPath entries don't exactly match the board names in the root AST, the board was removed from the AST after graph compilation, or boardPath contains wrong/renamed names.","commonSituations":"Passing a stale boardPath after the parent file was rewritten; typos or casing differences in layer/scenario/step names; mutating g.AST concurrently between GetBoardGraph and Create.","solutions":["Verify boardPath exactly matches the board names in the file (each element is a layer/scenario/step name as written in source).","Recompile g from the current file text so g.AST is in sync before calling Create.","Do not mutate or replace g.AST between obtaining the graph and calling Create.","Edit the root board (empty boardPath) if the board structure is ambiguous."],"exampleFix":"// before\ng2, _, err := d2oracle.Create(g, []string{\"Layers\", \"prod\"}, \"x\") // wrong casing -> AST not found\n// after\ng2, _, err := d2oracle.Create(g, []string{\"layers\", \"prod\"}, \"x\") // must match source names","handlingStrategy":"validation","validationCode":"func boardInAST(g *d2graph.Graph, boardPath []string) bool {\n    // confirm each name appears under layers/scenarios/steps in the source text\n    return len(boardPath) == 0 || d2oracle.GetBoardGraph(g, boardPath) != nil\n}","typeGuard":"func validBoardPath(boardPath []string) bool {\n    for _, p := range boardPath { if p == \"\" { return false } }\n    return true\n}","tryCatchPattern":"g2, _, err := d2oracle.Create(g, boardPath, key)\nif err != nil && strings.Contains(err.Error(), \"AST not found\") {\n    g, _, _ = recompileFromSource() // refresh AST, retry once\n}","preventionTips":["Keep boardPath exactly matching source board names","Never mutate g.AST between compile and oracle calls","Recompile g from current source before edits"],"tags":["go","d2","boards","ast"],"backgroundTag":"board-path-mismatch","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}