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

  1. Append the arrowhead property to the key: `(a -> b).target-arrowhead.shape` or `(a -> b).source-arrowhead.style.fill`.
  2. Validate that the path has at least two parts after the reserved keyword before calling.
  3. If you meant to toggle the arrowhead itself, use the `arrowhead` reserved key on the edge style instead (e.g. `style.source-arrowhead`).
  4. 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

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

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/163addcb0d704dd3. Report an issue: GitHub.