{"record":{"id":"fb02f1461d5065df","repo":"golang/go","slug":"empty-string-key-in-overlay-map","errorCode":null,"errorMessage":"empty string key in overlay map","messagePattern":"empty string key in overlay map","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/fsys/fsys.go","lineNumber":371,"sourceCode":"\tTrace(\"ReadFile\", OverlayFile)\n\tb, err := os.ReadFile(OverlayFile)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"reading overlay: %v\", err)\n\t}\n\treturn initFromJSON(b)\n}\n\nfunc initFromJSON(js []byte) error {\n\tvar ojs overlayJSON\n\tif err := json.Unmarshal(js, &ojs); err != nil {\n\t\treturn fmt.Errorf(\"parsing overlay JSON: %v\", err)\n\t}\n\n\tseen := make(map[string]string)\n\tvar list []replace\n\tfor _, from := range slices.Sorted(maps.Keys(ojs.Replace)) {\n\t\tif from == \"\" {\n\t\t\treturn fmt.Errorf(\"empty string key in overlay map\")\n\t\t}\n\t\tafrom := abs(from)\n\t\tif old, ok := seen[afrom]; ok {\n\t\t\treturn fmt.Errorf(\"duplicate paths %s and %s in overlay map\", old, from)\n\t\t}\n\t\tseen[afrom] = from\n\t\tlist = append(list, replace{from: afrom, to: abs(ojs.Replace[from])})\n\t}\n\n\tslices.SortFunc(list, func(x, y replace) int { return cmp(x.from, y.from) })\n\n\tfor i, r := range list {\n\t\tif r.to == \"\" { // deleted\n\t\t\tcontinue\n\t\t}\n\t\t// have file for r.from; look for child file implying r.from is a directory\n\t\tprefix := r.from + string(filepath.Separator)\n\t\tfor _, next := range list[i+1:] {","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/fsys/fsys.go#L353-L389","documentation":"Returned by fsys.initFromJSON when the overlay's 'replace' map contains an empty-string key. An empty source path is meaningless for path replacement and would match every file, so it is rejected outright during sorted iteration of the replace map.","triggerScenarios":"An overlay JSON whose replace object includes \"\": \"something\" as one of its entries.","commonSituations":"Programmatic overlay generators that emit an empty entry when the source path was unset; templating bugs; copy-paste artifacts leaving an empty key.","solutions":["Open the overlay JSON and remove or fill in the empty-string key under 'replace'.","If a generator builds the overlay, ensure no empty source paths are emitted (filter `if from == \"\" continue`).","Validate the overlay with a small script that rejects empty replace keys before invoking go."],"exampleFix":"// before — overlay.json\n{ \"replace\": { \"\": \"fallback.go\" } }\n// after — populate or omit the key\n{ \"replace\": { \"main.go\": \"fallback.go\" } }","handlingStrategy":"validation","validationCode":"for from := range ojs.Replace {\n    if from == \"\" {\n        return fmt.Errorf(\"overlay 'replace' contains an empty-string key\")\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When generating overlays, filter out empty source paths.","Lint the overlay JSON for empty keys before committing.","Treat an empty source path as a generator bug, not user input."],"tags":["overlay","json","validation","go-command"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}