d2lang/d2 · error
failed to recompile: %s %w
Error message
failed to recompile: %s %w
What it means
After AST edits, recompile serializes the mutated AST with d2format and recompiles it via d2compiler.Compile. If the edited AST is no longer valid D2 (malformed keys, broken references, invalid values), the compiler error is wrapped as 'failed to recompile:\n<formatted source>\n<cause>' so the caller can see both the exact post-edit source and the compiler diagnostics.
Source
Thrown at d2oracle/edit.go:321
// We don't want this to be underscore-resolved scope. We want to ignore underscores
var scopeak []string
if fromScope != g.Root {
scopek, err := d2parser.ParseKey(fromScope.AbsID())
if err != nil {
return nil, err
}
scopeak = d2graph.Key(scopek)
}
return pathFromScopeKey(g, key, scopeak)
}
func recompile(g *d2graph.Graph) (*d2graph.Graph, error) {
s := d2format.Format(g.AST)
g2, _, err := d2compiler.Compile(g.AST.Range.Path, strings.NewReader(s), &d2compiler.CompileOptions{
FS: g.FS,
})
if err != nil {
return nil, fmt.Errorf("failed to recompile:\n%s\n%w", s, err)
}
return g2, nil
}
// TODO merge flat styles
func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) error {
if tag != nil {
if hasSpace(*tag) {
return fmt.Errorf("spaces are not allowed in blockstring tags")
}
}
mk, err := d2parser.ParseMapKey(key)
if err != nil {
return err
}
if len(mk.Edges) > 1 {View on GitHub (pinned to 0d69dca6f5)
Solutions
- Read the embedded post-edit source and compiler message in the wrapped error to find the offending line/key.
- Fix the key/value passed to the oracle (correct syntax, valid identifier, type-correct value).
- Remove or update references (edges/connections) to deleted objects before/after the mutation.
- Validate the desired value against D2 docs (reserved keywords, style value types) before calling Set/Create.
Example fix
// before g2, err := d2oracle.Set(g, nil, "shape", nil, &str) // invalid value -> recompile fails // after g2, err := d2oracle.Set(g, nil, "x.shape", nil, &str) // valid key + valid scalar value
Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check key/value before oracle calls
if strings.ContainsAny(key, "\"") { return errors.New("invalid key") }
if tag != nil && hasSpace(*tag) { return errors.New("invalid tag") } Type guard
func oracleErrRecompile(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to recompile:")
} Try / catch
g2, err := d2oracle.Set(g, nil, key, tag, value)
if oracleErrRecompile(err) {
var src, cause string
fmt.Sscanf(err.Error(), "failed to recompile:\n%[^\n]\n%s", &src, &cause) // inspect post-edit source
log.Printf("oracle produced invalid D2: %v", err)
} Prevention
- Use valid D2 identifiers and type-correct values
- Remove dangling references before deleting objects
- Avoid reserved keywords as keys
- Always surface the wrapped compiler message to users
When it happens
Trigger: Any oracle mutation (Create, Set, ReconnectEdge, Delete, deleteReserved, deleteObjField) that leaves the AST in a state the D2 compiler rejects — e.g. setting a reserved/invalid key value, an edge key referencing a removed object, or malformed key syntax accepted by the parser but not the compiler.
Common situations: Setting values of wrong types (e.g. non-numeric into numeric fields); creating keys that collide with reserved words; deleting an object while edges still reference it; invalid blockstring content.
Related errors
- board %v cannot be modified through this file
- board %v AST not found
- failed to fully compile (partial render written) %s: %w
- failed to compile %s: %w
- dimensions for object label %#v not found
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/5cacfdafba86dbcb.
Report an issue: GitHub.