{"record":{"id":"b53ab425ec300775","repo":"Billionmail/BillionMail","slug":"error-saving-empty-site-map-file-v","errorCode":null,"errorMessage":"error saving empty site map file: %v","messagePattern":"error saving empty site map file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":614,"sourceCode":"\n// GetSiteMap retrieves the site map for a given domain from a JSON file.\n// It reads the site map file and returns a slice of SiteMap structs.\n// If the file does not exist or cannot be read, it returns an error.\nfunc GetSiteMap(Domain string) ([]SiteMap, error) {\n\tfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/sitemap.json\", Domain)\n\tif !public.FileExists(filename) {\n\t\t// If the site map file does not exist, return an empty slice\n\t\t// This allows the system to handle cases where the site map has not been set up\n\t\t// and avoids errors when trying to read a non-existent file.\n\t\t// It also allows the user to create a new site map without needing to handle file not found errors.\n\t\temptySiteMap := []SiteMap{}\n\t\temptySiteMapJson, err := json.MarshalIndent(emptySiteMap, \"\", \"  \")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error marshalling empty site map: %v\", err)\n\t\t}\n\t\terr = os.WriteFile(filename, emptySiteMapJson, 0644)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error saving empty site map file: %v\", err)\n\t\t}\n\t\treturn emptySiteMap, nil\n\t}\n\tdata, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading site map file: %v\", err)\n\t}\n\n\tvar siteMap []SiteMap\n\terr = json.Unmarshal(data, &siteMap)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshalling site map: %v\", err)\n\t}\n\treturn siteMap, nil\n}\n\nfunc SaveSiteMap(Domain string, siteMap []SiteMap) error {\n\tfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/sitemap.json\", Domain)","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L596-L632","documentation":"In the same self-healing branch of GetSiteMap, after marshalling an empty []SiteMap the function attempts os.WriteFile(filename, ..., 0644) to bootstrap sitemap.json. This error wraps the write failure — most commonly because the directory PRODUCT_CONFIG_PATH/<domain>/ does not exist, since GetSiteMap (like SaveStyleConfig) never creates it.","triggerScenarios":"First call to GetSiteMap (or callers GetSitemap, GetSitemapPrompt, AddSiteMapNode, RemoveSiteMapNode, AppendSitemap) for a domain with no sitemap.json and no PRODUCT_CONFIG_PATH/<domain>/ directory, or with a read-only/non-writable directory.","commonSituations":"Newly registered domain whose config folder was never created; service user lacks write permission; container volume mounted read-only; PRODUCT_CONFIG_PATH misconfigured to a non-existent base path.","solutions":["Create the domain config directory before writing: os.MkdirAll(filepath.Dir(filename), 0755) in GetSiteMap's bootstrap branch.","Check permissions/ownership of PRODUCT_CONFIG_PATH/<domain>/ for the service user (chown/chmod).","Confirm PRODUCT_CONFIG_PATH points to an existing, writable base directory.","Read the nested os error to confirm the cause (e.g. 'no such file or directory' vs 'permission denied')."],"exampleFix":"// before\nerr = os.WriteFile(filename, emptySiteMapJson, 0644)\nif err != nil { return nil, fmt.Errorf(\"error saving empty site map file: %v\", err) }\n// after\nif err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {\n    return nil, fmt.Errorf(\"error creating sitemap dir: %v\", err)\n}\nerr = os.WriteFile(filename, emptySiteMapJson, 0644)\nif err != nil { return nil, fmt.Errorf(\"error saving empty site map file: %v\", err) }","handlingStrategy":"retry","validationCode":"func ensureSitemapWritable(domain string) error {\n    dir := filepath.Join(PRODUCT_CONFIG_PATH, domain)\n    if err := os.MkdirAll(dir, 0755); err != nil { return err }\n    f, err := os.CreateTemp(dir, \".wtest*\")\n    if err != nil { return err }\n    f.Close(); os.Remove(f.Name())\n    return nil\n}\n// call before GetSiteMap on fresh domains","typeGuard":null,"tryCatchPattern":"m, err := GetSiteMap(domain)\nif err != nil && strings.Contains(err.Error(), \"error saving empty site map file\") {\n    // create the dir, then retry once\n    os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, domain), 0755)\n    m, err = GetSiteMap(domain)\n}","preventionTips":["Create the domain config directory at domain-provisioning time so the bootstrap write always succeeds.","Keep PRODUCT_CONFIG_PATH on a read-write mount with adequate disk space.","Fix GetSiteMap itself to MkdirAll before the bootstrap write — the safest guard."],"tags":["go","file-io","filesystem","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}