d2lang/d2 · error
can only set one edge at a time
Error message
can only set one edge at a time
What it means
d2oracle's Set/Create path (`_set`) supports addressing at most one edge per key. The parsed key contained a multi-edge chain (e.g. `(a -> b -> c)` produces two edges), which is ambiguous for setting a single value, so it is rejected.
Source
Thrown at d2oracle/edit.go:340
}
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 {
return errors.New("can only set one edge at a time")
}
if value != nil {
mk.Value = d2ast.MakeValueBox(d2ast.RawString(*value, false))
} else {
mk.Value = d2ast.ValueBox{}
}
if tag != nil && value != nil {
mk.Value = d2ast.MakeValueBox(&d2ast.BlockString{
Tag: *tag,
Value: *value,
})
}
scope := baseAST
edgeTrimCommon(mk)
obj := g.Root
toSkip := 1View on GitHub (pinned to 0d69dca6f5)
Solutions
- Split the chain and address a single edge with an index: `(a -> b -> c)[0].style.opacity` instead of `(a -> b -> c).style.opacity`.
- Perform one Set call per edge segment.
- If the goal is to set an attribute of an object rather than an edge, drop the edge portion from the key.
- Validate len(mk.Edges) <= 1 client-side before calling Set/Create.
Example fix
// before g, err = d2oracle.Set(g, nil, "(a -> b -> c).style.opacity", &val) // after g, err = d2oracle.Set(g, nil, "(a -> b -> c)[0].style.opacity", &val)
Defensive patterns
Strategy: validation
Validate before calling
mk, err := d2parser.ParseMapKey(key)
if err != nil { return err }
if len(mk.Edges) > 1 {
return fmt.Errorf("key %q contains %d edges; split and address one with an [index]", key, len(mk.Edges))
} Type guard
func isSingleEdgeKey(key string) bool {
mk, err := d2parser.ParseMapKey(key)
return err == nil && len(mk.Edges) <= 1
} Try / catch
err := d2oracle.Set(g, nil, key, &val)
if err != nil && err.Error() == "can only set one edge at a time" {
return fmt.Errorf("split chain key %q into indexed single-edge keys", key)
} Prevention
- Never pass chain keys like (a -> b -> c) to Set/Create; always add an [index]
- Sanitize user-provided keys in editors before forwarding to d2oracle
- Add a unit test asserting all generated keys parse to a single edge
- Remember <-> also composes chains when repeated
When it happens
Trigger: Calling d2oracle.Set or d2oracle.Create with a key containing a chain of two or more edges, e.g. `x.(a -> b -> c).style.opacity` or `(a <-> b <-> c)`.
Common situations: Users typing chained edges in a UI that forwards the raw key to Set; code generators that compose chains from path data; copying a valid D2 document key into the oracle API without splitting it.
Related errors
- edge key must be reserved
- can only delete one edge at a time
- edgeKey must be an edge
- edgeKey must refer to an existing edge
- edge not found
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/9f81d8106304c0f9.
Report an issue: GitHub.