{"record":{"id":"1f5cc00f257744be","repo":"d2lang/d2","slug":"empty-value-q","errorCode":null,"errorMessage":"empty value: %q","messagePattern":"empty value: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2parser/parse.go","lineNumber":129,"sourceCode":"\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 {\n\t\treturn nil, fmt.Errorf(\"empty value: %q\", value)\n\t}\n\treturn v.Unbox(), nil\n}\n\n// TODO: refactor parser to keep entire file in memory as []rune\n//   - trivial to then convert positions\n//   - lookahead is gone, just forward back as much as you want :)\n//   - streaming parser isn't really helpful.\n//   - just read into a string even and decode runes forward/back as needed\n//   - the whole file essentially exists within the parser as the AST anyway...\n//\n// TODO: ast struct that combines map & errors and pass that around\ntype parser struct {\n\tpath     string\n\tpos      d2ast.Position\n\tutf16Pos bool\n\n\treader    io.RuneReader","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2parser/parse.go#L111-L147","documentation":"ParseValue returns this when the input parses without errors but the resulting value unboxes to nil, i.e. the string is empty or resolves to nothing. The library treats a nil value as invalid input for a value context.","triggerScenarios":"Calling d2parser.ParseValue with \"\" or a string that parses to a nil/unboxed value (e.g. only whitespace or a construct producing no value).","commonSituations":"Building D2 documents from template/config data where a field is missing and an empty string is passed through unchecked.","solutions":["Check for empty/whitespace input before calling ParseValue","Supply a default value when the source field is empty","If emptiness is legitimate, handle it before parsing instead of relying on the error"],"exampleFix":"// before\nv, _ := d2parser.ParseValue(cfg.Label) // panics-free but err=\"empty value\"\n// after\nif strings.TrimSpace(cfg.Label) == \"\" {\n    return errors.New(\"label is required\")\n}\nv, err := d2parser.ParseValue(cfg.Label)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(value) == \"\" {\n    // skip parsing or substitute a default\n    value = defaultLabel\n}","typeGuard":null,"tryCatchPattern":"v, err := d2parser.ParseValue(value)\nif err != nil {\n    if strings.Contains(err.Error(), \"empty value\") {\n        return defaultFallback, nil\n    }\n    return err\n}","preventionTips":["Guard empty config fields before parsing","Apply defaults for optional D2 values","Trim whitespace-only inputs early"],"tags":["d2","parser","empty-input"],"backgroundTag":"empty-value","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}