{"record":{"id":"9160b857d254fd1f","repo":"Billionmail/BillionMail","slug":"error-saving-site-map-file-v","errorCode":null,"errorMessage":"error saving site map file: %v","messagePattern":"error saving site map file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":639,"sourceCode":"\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)\n\tdata, err := json.MarshalIndent(siteMap, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling site map: %v\", err)\n\t}\n\terr = os.WriteFile(filename, data, 0644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error saving site map file: %v\", err)\n\t}\n\treturn nil\n}\n\n// AddSiteMapNode adds a new site map node with the provided title and URI path to the site map of the specified domain.\n// It reads the existing site map, appends the new node, and saves the updated site map back to the file.\n// If the site map file does not exist or cannot be read, it returns an error.\nfunc AddSiteMapNode(Domain string, Title string, UriPath string) error {\n\tsiteMap, err := GetSiteMap(Domain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting site map: %v\", err)\n\t}\n\n\tnewNode := SiteMap{\n\t\tTitle:      Title,\n\t\tUriPath:    UriPath,\n\t\tUpdateTime: public.GetNowTime(),\n\t}","sourceCodeStart":621,"sourceCodeEnd":657,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L621-L657","documentation":"SaveSiteMap writes the marshalled sitemap to PRODUCT_CONFIG_PATH/<domain>/sitemap.json via os.WriteFile with 0644. This error wraps any write failure: the domain config directory missing (SaveSiteMap never creates it), permission denial, read-only filesystem, or disk-full.","triggerScenarios":"Calling SaveSiteMap (or AppendSitemap) when PRODUCT_CONFIG_PATH/<domain>/ does not exist (ENOENT), when the process cannot write there (EACCES/EPERM), or on a full/read-only filesystem.","commonSituations":"Adding a sitemap node for a domain whose config directory was never provisioned; running under a different user after a migration; Docker volume mounted read-only; disk quota exceeded.","solutions":["Create the directory before writing: os.MkdirAll(filepath.Dir(filename), 0755) in SaveSiteMap or at domain provisioning.","Check ownership/permissions of PRODUCT_CONFIG_PATH/<domain>/ (chown/chmod for the service user).","Confirm the nested os error text to distinguish 'no such file or directory' from 'permission denied' or 'no space left on device'.","Free disk space or remount the volume read-write if the cause is disk/quota related."],"exampleFix":"// before\nfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/sitemap.json\", Domain)\ndata, _ := json.MarshalIndent(siteMap, \"\", \"  \")\nerr := os.WriteFile(filename, data, 0644)\n// after\nfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/sitemap.json\", Domain)\nif err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {\n    return fmt.Errorf(\"error creating sitemap dir: %v\", err)\n}\ndata, _ := json.MarshalIndent(siteMap, \"\", \"  \")\nerr := os.WriteFile(filename, data, 0644)","handlingStrategy":"try-catch","validationCode":"func ensureSitemapDir(domain string) error {\n    dir := filepath.Join(PRODUCT_CONFIG_PATH, domain)\n    if err := os.MkdirAll(dir, 0755); err != nil { return err }\n    if info, err := os.Stat(dir); err != nil || !info.IsDir() {\n        return fmt.Errorf(\"%s not writable dir\", dir)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := SaveSiteMap(domain, siteMap); err != nil {\n    switch {\n    case errors.Is(err, os.ErrNotExist):\n        os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, domain), 0755)\n        err = SaveSiteMap(domain, siteMap) // retry once after mkdir\n    case errors.Is(err, os.ErrPermission):\n        log.Printf(\"permission denied writing sitemap for %s\", domain)\n    }\n    return err\n}","preventionTips":["Have SaveSiteMap MkdirAll its target directory before writing.","Keep config volumes read-write and monitor disk usage/quota.","Chown PRODUCT_CONFIG_PATH to the service user in deployment scripts."],"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"}