d2lang/d2 · error
malformed style setting, expected >= 2 part path
Error message
malformed style setting, expected >= 2 part path
What it means
When setting an arrowhead attribute (source-arrowhead/target-arrowhead) on an edge, the key must have exactly the reserved part plus a target property (at least 2 remaining path parts, e.g. `...source-arrowhead.shape`). Fewer parts means no property was named, so the set is rejected.
Source
Thrown at d2oracle/edit.go:757
return nil
}
case "source-arrowhead", "target-arrowhead":
var arrowhead *d2graph.Attributes
if reservedKey == "source-arrowhead" {
if edge.SrcArrowhead != nil {
attrs = *edge.SrcArrowhead
}
arrowhead = edge.SrcArrowhead
} else {
if edge.DstArrowhead != nil {
attrs = *edge.DstArrowhead
}
arrowhead = edge.DstArrowhead
}
if arrowhead != nil {
if reservedTargetKey == "" {
if len(mk.Key.Path[reservedIndex:]) < 2 {
return errors.New("malformed style setting, expected >= 2 part path")
}
reservedTargetKey = mk.Key.Path[reservedIndex+1].Unbox().ScalarString()
}
switch reservedTargetKey {
case "shape":
if inlined(&arrowhead.Shape) {
arrowhead.Shape.MapKey.SetScalar(mk.Value.ScalarBox())
return nil
}
case "label":
if inlined(&arrowhead.Label) {
arrowhead.Label.MapKey.SetScalar(mk.Value.ScalarBox())
return nil
}
case "style":
reservedTargetKey = mk.Key.Path[len(mk.Key.Path)-1].Unbox().ScalarString()
if inlined(attrs.Style.Filled) {
attrs.Style.Filled.MapKey.SetScalar(mk.Value.ScalarBox())View on GitHub (pinned to 0d69dca6f5)
Solutions
- Append the arrowhead property to the key: `(a -> b).target-arrowhead.shape` or `(a -> b).source-arrowhead.style.fill`.
- Validate that the path has at least two parts after the reserved keyword before calling.
- If you meant to toggle the arrowhead itself, use the `arrowhead` reserved key on the edge style instead (e.g. `style.source-arrowhead`).
- Ensure the key was not truncated during string construction.
Example fix
// before g, err = d2oracle.Set(g, nil, "(a -> b).target-arrowhead", &val) // after g, err = d2oracle.Set(g, nil, "(a -> b).target-arrowhead.shape", &val)
Defensive patterns
Strategy: validation
Validate before calling
mk, err := d2parser.ParseMapKey(key)
if err != nil { return err }
for i, p := range mk.Key.Path {
s := p.Unbox().ScalarString()
if s == "source-arrowhead" || s == "target-arrowhead" {
if len(mk.Key.Path)-i < 2 {
return fmt.Errorf("arrowhead key %q needs a trailing property, e.g. .shape", key)
}
}
} Type guard
func arrowheadKeyWellFormed(key string) bool {
mk, err := d2parser.ParseMapKey(key)
if err != nil { return false }
for i, p := range mk.Key.Path {
s := p.Unbox().ScalarString()
if s == "source-arrowhead" || s == "target-arrowhead" {
return len(mk.Key.Path)-i >= 2
}
}
return true
} Try / catch
err := d2oracle.Set(g, nil, key, &val)
if err != nil && strings.Contains(err.Error(), "malformed style setting, expected >= 2 part path") {
return fmt.Errorf("append the arrowhead property (shape, style.fill, ...) to %q", key)
} Prevention
- Always name the property after source-arrowhead/target-arrowhead (e.g. .shape)
- Build arrowhead keys from a constant schema, not free-form strings
- To toggle arrowheads themselves use the edge style arrowhead keyword instead
- Add path-length assertions in key-builder helpers
When it happens
Trigger: Calling d2oracle.Set/Create with a key like `(a -> b).source-arrowhead` (no trailing property) or with only one path element after the reserved index; the code requires len(mk.Key.Path[reservedIndex:]) >= 2.
Common situations: Users trying to style an arrowhead as a whole instead of a specific property; UI builders that truncate the path; forgetting the second part like `.shape` or `.style.fill` after `source-arrowhead`.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- malformed style setting, expected 2 part path
- edgeKey must be an edge
- edgeKey must refer to an existing edge
- edge not found
- newSrc not found
AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31).
Data as JSON: /api/errors/163addcb0d704dd3.
Report an issue: GitHub.