d2lang/d2 · error
board %v AST not found
Error message
board %v AST not found
What it means
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.
Source
Thrown at d2oracle/edit.go:64
return nil, "", fmt.Errorf("board %v cannot be modified through this file", boardPath)
}
}
newKey, edge, err := generateUniqueKey(boardG, key, nil, nil)
if err != nil {
return nil, "", err
}
if edge {
err = _set(boardG, baseAST, key, nil, nil)
} else {
err = _set(boardG, baseAST, newKey, nil, nil)
}
if len(boardPath) > 0 {
replaced := ReplaceBoardNode(g.AST, baseAST, boardPath)
if !replaced {
return nil, "", fmt.Errorf("board %v AST not found", boardPath)
}
}
if err != nil {
return nil, "", err
}
g, err = recompile(g)
if err != nil {
return nil, "", err
}
return g, newKey, nil
}
// TODO: update graph in place when compiler can accept single modifications
// TODO: go through all references to decide best spot to insert something
func Set(g *d2graph.Graph, boardPath []string, key string, tag, value *string) (_ *d2graph.Graph, err error) {
var valueHelp string
if value == nil {View on GitHub (pinned to 0d69dca6f5)
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.
Example fix
// before
g2, _, err := d2oracle.Create(g, []string{"Layers", "prod"}, "x") // wrong casing -> AST not found
// after
g2, _, err := d2oracle.Create(g, []string{"layers", "prod"}, "x") // must match source names Defensive patterns
Strategy: validation
Validate before calling
func boardInAST(g *d2graph.Graph, boardPath []string) bool {
// confirm each name appears under layers/scenarios/steps in the source text
return len(boardPath) == 0 || d2oracle.GetBoardGraph(g, boardPath) != nil
} Type guard
func validBoardPath(boardPath []string) bool {
for _, p := range boardPath { if p == "" { return false } }
return true
} Try / catch
g2, _, err := d2oracle.Create(g, boardPath, key)
if err != nil && strings.Contains(err.Error(), "AST not found") {
g, _, _ = recompileFromSource() // refresh AST, retry once
} Prevention
- Keep boardPath exactly matching source board names
- Never mutate g.AST between compile and oracle calls
- Recompile g from current source before edits
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- board %v cannot be modified through this file
- failed to recompile: %s %w
- board names cannot contain dots or quotes
- dimensions for object label %#v not found
- dimensions for class field %#v not found
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/9abc635a09c404d6.
Report an issue: GitHub.