{"record":{"id":"73ef21b81f76ca7b","repo":"d2lang/d2","slug":"failed-to-parse-map-key-q-w","errorCode":null,"errorMessage":"failed to parse map key %q: %w","messagePattern":"failed to parse map key %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2parser/parse.go","lineNumber":110,"sourceCode":"\tk := p.parseKey()\n\tif !p.err.Empty() {\n\t\treturn nil, fmt.Errorf(\"failed to parse key %q: %w\", key, p.err)\n\t}\n\tif k == nil {\n\t\treturn nil, fmt.Errorf(\"empty key: %q\", key)\n\t}\n\treturn k, nil\n}\n\nfunc ParseMapKey(mapKey string) (*d2ast.Key, error) {\n\tp := &parser{\n\t\treader: strings.NewReader(mapKey),\n\t\terr:    &ParseError{},\n\t}\n\n\tmk := p.parseMapKey()\n\tif !p.err.Empty() {\n\t\treturn nil, fmt.Errorf(\"failed to parse map key %q: %w\", mapKey, p.err)\n\t}\n\tif mk == nil {\n\t\treturn nil, fmt.Errorf(\"empty map key: %q\", mapKey)\n\t}\n\treturn mk, nil\n}\n\nfunc ParseValue(value string) (d2ast.Value, error) {\n\tp := &parser{\n\t\treader: strings.NewReader(value),\n\t\terr:    &ParseError{},\n\t}\n\n\tv := p.parseValue()\n\tif !p.err.Empty() {\n\t\treturn nil, fmt.Errorf(\"failed to parse value %q: %w\", value, p.err)\n\t}\n\tif v.Unbox() == nil {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2parser/parse.go#L92-L128","documentation":"ParseMapKey parses a string intended to be a map key with an optional inner map (e.g. 'x: {\\n y\\n}') into a d2ast.Key. If the parser records any diagnostics during parseMapKey, the error is wrapped with this message and %w, preserving the underlying ParseError.","triggerScenarios":"Calling d2parser.ParseMapKey with text whose map-key syntax is invalid: unbalanced braces, a value line where a key was expected, bad quoting, or trailing content that cannot form a key/map.","commonSituations":"Constructing map-key snippets dynamically for d2oracle edits; passing a full board fragment including connections where only a key is allowed; newline/indentation mistakes when templates generate the snippet.","solutions":["Unwrap the error to see the exact parse position/token","Ensure braces are balanced and the snippet is a valid key with optional map block","Validate the snippet against d2fmt to normalize whitespace/newlines before parsing","Build keys/maps with d2aster AST constructors rather than string templates"],"exampleFix":"// before\nmk, err := d2parser.ParseMapKey(\"x: {\") // unbalanced brace\n// after\nmk, err := d2parser.ParseMapKey(\"x: {\\n  y\\n}\")","handlingStrategy":"try-catch","validationCode":"func snippetIsBalanced(s string) bool {\n    open := strings.Count(s, \"{\")\n    close := strings.Count(s, \"}\")\n    return open == close && strings.TrimSpace(s) != \"\"\n}\n// if !snippetIsBalanced(snippet) { fix snippet before ParseMapKey }","typeGuard":null,"tryCatchPattern":"mk, err := d2parser.ParseMapKey(snippet)\nif err != nil {\n    var parseErr *d2parser.ParseError\n    if u := errors.Unwrap(err); u != nil {\n        if pe, ok := u.(*d2parser.ParseError); ok {\n            parseErr = pe\n        }\n    }\n    return fmt.Errorf(\"bad map key snippet %q: %v\", snippet, parseErr)\n}","preventionTips":["Balance-check braces in generated snippets before parsing","Pass snippets through d2fmt to normalize formatting first","Only pass key(+map) text, never full boards or connections","Use AST builders (d2aster) for programmatic construction"],"tags":["d2","parser","map-key-syntax"],"backgroundTag":"key-parse-failed","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}