{"record":{"id":"0fe57d77290c3b31","repo":"d2lang/d2","slug":"board-names-cannot-contain-dots-or-quotes","errorCode":null,"errorMessage":"board names cannot contain dots or quotes","messagePattern":"board names cannot contain dots or quotes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"d2oracle/edit.go","lineNumber":428,"sourceCode":"\t\t\t\t// If we find a deeper obj.Map we need to skip this key too.\n\t\t\t\ttoSkip++\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tscope = maybeNewScope\n\t\t\tmk.Key.Path = mk.Key.Path[toSkip:]\n\t\t\ttoSkip = 1\n\t\t\tif len(mk.Key.Path) == 0 {\n\t\t\t\tmk.Key = nil\n\t\t\t}\n\t\t}\n\n\t\tif mk.Key != nil && len(mk.Key.Path) == 2 {\n\t\t\tboardType := mk.Key.Path[0].Unbox().ScalarString()\n\t\t\tif boardType == \"layers\" || boardType == \"scenarios\" || boardType == \"steps\" {\n\t\t\t\tboardName := mk.Key.Path[1].Unbox().ScalarString()\n\t\t\t\tif strings.ContainsAny(boardName, \".\\\"\") {\n\t\t\t\t\treturn fmt.Errorf(\"board names cannot contain dots or quotes\")\n\t\t\t\t}\n\t\t\t\t// Force map structure\n\t\t\t\tvar containerMap *d2ast.Map\n\t\t\t\tfor _, n := range scope.Nodes {\n\t\t\t\t\tif n.MapKey != nil && n.MapKey.Key != nil && len(n.MapKey.Key.Path) == 1 &&\n\t\t\t\t\t\tn.MapKey.Key.Path[0].Unbox().ScalarString() == boardType {\n\t\t\t\t\t\tcontainerMap = n.MapKey.Value.Map\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif containerMap == nil {\n\t\t\t\t\tcontainerMap = &d2ast.Map{\n\t\t\t\t\t\tRange: d2ast.MakeRange(\",1:0:0-1:0:0\"),\n\t\t\t\t\t}\n\t\t\t\t\tcontainerMK := &d2ast.Key{\n\t\t\t\t\t\tKey: &d2ast.KeyPath{\n\t\t\t\t\t\t\tPath: []*d2ast.StringBox{","sourceCodeStart":410,"sourceCodeEnd":446,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/d2oracle/edit.go#L410-L446","documentation":"When the key targets a board (first path element is layers/scenarios/steps), _set validates the board name (second path element). D2 boards are referenced by path in tooling, so dots would be ambiguous with nesting and quotes break naming; a board name containing '.' or '\"' is rejected with this error.","triggerScenarios":"Calling Set or Create with a key like `layers.my.board` or `layers.\"x\".field` — any board name segment under layers/scenarios/steps containing a dot or double quote.","commonSituations":"Using dotted names borrowed from file paths or domain names for layers; programmatic key construction that interpolates un-sanitized names containing dots.","solutions":["Rename the board to remove dots/quotes (use hyphens or underscores instead).","Sanitize generated board names before building the key: replace '.' and '\"' characters.","Target the field within the board with a key that only uses the sanitized board name."],"exampleFix":"// before\nkey := \"layers.app.config.db.host\" // board name \"app\" has dot\n// after\nkey := \"layers_app_config.db.host\" // or rename boards to avoid dots","handlingStrategy":"validation","validationCode":"func validBoardName(name string) bool {\n    return !strings.ContainsAny(name, \".\\\"\")\n}\n// before building key: if !validBoardName(boardName) { reject }","typeGuard":"func safeBoardKey(boardType, boardName, rest string) (string, bool) {\n    if strings.ContainsAny(boardName, \".\\\"\") { return \"\", false }\n    return boardType + \".\" + boardName + rest, true\n}","tryCatchPattern":"g2, err := d2oracle.Set(g, boardPath, key, tag, value)\nif err != nil && strings.Contains(err.Error(), \"board names cannot contain dots or quotes\") {\n    return fmt.Errorf(\"sanitize board name in key %q\", key)\n}","preventionTips":["Sanitize generated board names (replace '.' and '\"' with '-')","Validate board names at input boundaries (UI/CLI)","Prefer hyphens/underscores for multi-word board names"],"tags":["go","d2","boards","naming","validation"],"backgroundTag":"invalid-board-name","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}