{"record":{"id":"4144bfd6026f031a","repo":"Billionmail/BillionMail","slug":"error-marshalling-empty-site-map-v","errorCode":null,"errorMessage":"error marshalling empty site map: %v","messagePattern":"error marshalling empty site map: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/internal/service/askai/project.go","lineNumber":610,"sourceCode":"\t\treturn fmt.Errorf(\"error saving style config file: %v\", err)\n\t}\n\treturn nil\n}\n\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","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L592-L628","documentation":"When PRODUCT_CONFIG_PATH/<domain>/sitemap.json does not exist, GetSiteMap self-heals by marshalling an empty []SiteMap and writing it to the file. This error wraps a json.MarshalIndent failure on that empty slice. Marshalling an empty slice of plain structs cannot fail, so this is a defensive branch that only fires if SiteMap gains unmarshalable fields (chan, func, cycles).","triggerScenarios":"First access to the sitemap for a domain with no sitemap.json, combined with a SiteMap type that contains a value json cannot encode (only possible after type changes).","commonSituations":"Hit only by developers modifying SiteMap — e.g. adding a callback field or a cyclic pointer — then calling GetSiteMap/GetSitemapPrompt/AddSiteMapNode on a fresh domain.","solutions":["Inspect the wrapped message ('json: unsupported type: ...') to identify the offending SiteMap field.","Remove/replace the unmarshalable field in SiteMap with a serializable type.","As a belt-and-braces measure, replace the marshal-and-save fallback with writing a literal []byte(\"[]\\n\") constant, which can never fail marshalling."],"exampleFix":"// before\nemptySiteMapJson, err := json.MarshalIndent(emptySiteMap, \"\", \"  \")\nif err != nil { return nil, fmt.Errorf(\"error marshalling empty site map: %v\", err) }\n// after\nemptySiteMapJson := []byte(\"[]\\n\") // an empty JSON array is always valid; no marshal step to fail","handlingStrategy":"fallback","validationCode":"// Not triggerable at runtime with current SiteMap; add a regression test:\nb, err := json.Marshal([]SiteMap{})\nif err != nil || string(b) != \"[]\" { t.Fatalf(\"empty SiteMap must marshal to []\") }","typeGuard":"func isMarshalable(v any) bool {\n    _, err := json.Marshal(v)\n    return err == nil\n}\n// usage: if !isMarshalable(emptySiteMap) { ... }","tryCatchPattern":"m, err := GetSiteMap(domain)\nif err != nil {\n    if strings.Contains(err.Error(), \"error marshalling empty site map\") {\n        // struct defect: do not retry, fix SiteMap definition\n    }\n    return err\n}","preventionTips":["Prefer writing the literal []byte(\"[]\\n\") for the bootstrap file to eliminate the marshal step entirely.","Keep SiteMap fields JSON-serializable; test with a fully populated instance in CI.","Treat any occurrence of this error as a code bug, not an environment issue."],"tags":["go","json","serialization","sitemap"],"backgroundTag":"json-unsupported-type","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"}