{"record":{"id":"163addcb0d704dd3","repo":"d2lang/d2","slug":"malformed-style-setting-expected-2-part-path","errorCode":null,"errorMessage":"malformed style setting, expected >= 2 part path","messagePattern":"malformed style setting, expected >= 2 part path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2oracle/edit.go","lineNumber":757,"sourceCode":"\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\tcase \"source-arrowhead\", \"target-arrowhead\":\n\t\t\t\tvar arrowhead *d2graph.Attributes\n\t\t\t\tif reservedKey == \"source-arrowhead\" {\n\t\t\t\t\tif edge.SrcArrowhead != nil {\n\t\t\t\t\t\tattrs = *edge.SrcArrowhead\n\t\t\t\t\t}\n\t\t\t\t\tarrowhead = edge.SrcArrowhead\n\t\t\t\t} else {\n\t\t\t\t\tif edge.DstArrowhead != nil {\n\t\t\t\t\t\tattrs = *edge.DstArrowhead\n\t\t\t\t\t}\n\t\t\t\t\tarrowhead = edge.DstArrowhead\n\t\t\t\t}\n\t\t\t\tif arrowhead != nil {\n\t\t\t\t\tif reservedTargetKey == \"\" {\n\t\t\t\t\t\tif len(mk.Key.Path[reservedIndex:]) < 2 {\n\t\t\t\t\t\t\treturn errors.New(\"malformed style setting, expected >= 2 part path\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treservedTargetKey = mk.Key.Path[reservedIndex+1].Unbox().ScalarString()\n\t\t\t\t\t}\n\t\t\t\t\tswitch reservedTargetKey {\n\t\t\t\t\tcase \"shape\":\n\t\t\t\t\t\tif inlined(&arrowhead.Shape) {\n\t\t\t\t\t\t\tarrowhead.Shape.MapKey.SetScalar(mk.Value.ScalarBox())\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"label\":\n\t\t\t\t\t\tif inlined(&arrowhead.Label) {\n\t\t\t\t\t\t\tarrowhead.Label.MapKey.SetScalar(mk.Value.ScalarBox())\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"style\":\n\t\t\t\t\t\treservedTargetKey = mk.Key.Path[len(mk.Key.Path)-1].Unbox().ScalarString()\n\t\t\t\t\t\tif inlined(attrs.Style.Filled) {\n\t\t\t\t\t\t\tattrs.Style.Filled.MapKey.SetScalar(mk.Value.ScalarBox())","sourceCodeStart":739,"sourceCodeEnd":775,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2oracle/edit.go#L739-L775","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`.","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."],"exampleFix":"// before\ng, err = d2oracle.Set(g, nil, \"(a -> b).target-arrowhead\", &val)\n// after\ng, err = d2oracle.Set(g, nil, \"(a -> b).target-arrowhead.shape\", &val)","handlingStrategy":"validation","validationCode":"mk, err := d2parser.ParseMapKey(key)\nif err != nil { return err }\nfor i, p := range mk.Key.Path {\n    s := p.Unbox().ScalarString()\n    if s == \"source-arrowhead\" || s == \"target-arrowhead\" {\n        if len(mk.Key.Path)-i < 2 {\n            return fmt.Errorf(\"arrowhead key %q needs a trailing property, e.g. .shape\", key)\n        }\n    }\n}","typeGuard":"func arrowheadKeyWellFormed(key string) bool {\n    mk, err := d2parser.ParseMapKey(key)\n    if err != nil { return false }\n    for i, p := range mk.Key.Path {\n        s := p.Unbox().ScalarString()\n        if s == \"source-arrowhead\" || s == \"target-arrowhead\" {\n            return len(mk.Key.Path)-i >= 2\n        }\n    }\n    return true\n}","tryCatchPattern":"err := d2oracle.Set(g, nil, key, &val)\nif err != nil && strings.Contains(err.Error(), \"malformed style setting, expected >= 2 part path\") {\n    return fmt.Errorf(\"append the arrowhead property (shape, style.fill, ...) to %q\", key)\n}","preventionTips":["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"],"tags":["edge","arrowhead","malformed-key","d2"],"backgroundTag":"malformed-style-path","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}