{"record":{"id":"f2a0131ace4a8c88","repo":"Billionmail/BillionMail","slug":"error-getting-site-map-v","errorCode":null,"errorMessage":"error getting site map: %v","messagePattern":"error getting site map: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":650,"sourceCode":"\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}\n\tsiteMap = append(siteMap, newNode)\n\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}","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L632-L668","documentation":"AddSiteMapNode reads the current sitemap via GetSiteMap and wraps any GetSiteMap failure with 'error getting site map: %v'. The root cause is always one of the underlying GetSiteMap errors: bootstrap write failure (missing/unwritable dir), read failure (deleted file/permissions), or unmarshal failure (corrupt JSON). The error therefore appears doubly wrapped and must be unwound to diagnose.","triggerScenarios":"Calling AddSiteMapNode (or AddSitemapNode) for a domain where sitemap.json cannot be read or created: missing PRODUCT_CONFIG_PATH/<domain>/ directory, unreadable sitemap.json, or corrupt/invalid JSON content.","commonSituations":"First node added to a brand-new domain whose directory does not exist (triggers the bootstrap write failure path); corrupt sitemap.json from a prior crash or manual edit; permission changes after a user switch.","solutions":["Unwrap the chain (%v inside %v) and match the inner cause: create the domain dir (MkdirAll), fix file permissions, or repair/delete corrupt sitemap.json.","Fix the corresponding GetSiteMap root cause first — this wrapper adds no handling of its own.","Validate sitemap.json with jq before re-attempting the node add if corruption is suspected.","Add a guard in AddSitemapNode's caller to provision the domain config directory during domain creation, so first-write never fails."],"exampleFix":"// before\nsiteMap, err := GetSiteMap(Domain)\nif err != nil {\n    return fmt.Errorf(\"error getting site map: %v\", err)\n}\n// after\nif err := os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, Domain), 0755); err != nil {\n    return fmt.Errorf(\"error ensuring config dir: %v\", err)\n}\nsiteMap, err := GetSiteMap(Domain)\nif err != nil {\n    return fmt.Errorf(\"error getting site map: %w\", err) // %w preserves the wrapped cause for errors.Is/As\n}","handlingStrategy":"try-catch","validationCode":"func canAddSitemapNode(domain string) error {\n    dir := filepath.Join(PRODUCT_CONFIG_PATH, domain)\n    if err := os.MkdirAll(dir, 0755); err != nil { return err }\n    p := filepath.Join(dir, \"sitemap.json\")\n    if data, err := os.ReadFile(p); err == nil {\n        var m []SiteMap\n        if err := json.Unmarshal(data, &m); err != nil {\n            return fmt.Errorf(\"corrupt sitemap: %w\", err)\n        }\n    }\n    return nil // missing file is fine: GetSiteMap bootstraps it\n}","typeGuard":null,"tryCatchPattern":"if err := AddSiteMapNode(domain, title, uri); err != nil {\n    log.Printf(\"add node failed: %v\", err) // full wrapped chain\n    switch {\n    case strings.Contains(err.Error(), \"error saving empty site map file\"):\n        os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, domain), 0755)\n    case strings.Contains(err.Error(), \"error unmarshalling site map\"):\n        // quarantine corrupt file, then retry\n        os.Rename(filepath.Join(PRODUCT_CONFIG_PATH, domain, \"sitemap.json\"), \".../sitemap.json.bad\")\n    }\n    return err\n}","preventionTips":["Provision the domain config directory at domain creation so first read/bootstrap always succeeds.","Use %w instead of %v at wrap sites so errors.Is/As can match root causes across the chain.","Validate sitemap.json integrity before bulk node additions.","Keep GetSiteMap healthy — this error is purely a proxy for its failures."],"tags":["go","error-wrapping","file-io","sitemap"],"backgroundTag":"wrapped-root-cause-error","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}