{"record":{"id":"2a07f4724ee05901","repo":"Billionmail/BillionMail","slug":"error-marshalling-images-v","errorCode":null,"errorMessage":"error marshalling images: %v","messagePattern":"error marshalling images: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":896,"sourceCode":"type BotStyle struct {\n\tAccentColor         string `json:\"accent_color\"`\n\tBodyFont            string `json:\"body_font\"`\n\tContainerBackground string `json:\"container_background\"`\n\tHeadingFont         string `json:\"heading_font\"`\n\tLinkFooterColor     string `json:\"link_footer_color\"`\n\tLinkSocialColor     string `json:\"link_social_color\"`\n\tPageBackground      string `json:\"page_background\"`\n\tTextColor           string `json:\"text_color\"`\n}\n\nfunc SaveImagesConfig(Domain string, images []ImageInfo) error {\n\timagesPath := fmt.Sprintf(\"%s/%s/images.json\", PRODUCT_CONFIG_PATH, Domain)\n\tif !public.FileExists(PRODUCT_CONFIG_PATH + \"/\" + Domain) {\n\t\tos.MkdirAll(PRODUCT_CONFIG_PATH+\"/\"+Domain, os.ModePerm)\n\t}\n\tdata, err := json.MarshalIndent(images, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling images: %v\", err)\n\t}\n\terr = os.WriteFile(imagesPath, data, 0644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error saving images file: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc ReadImagesConfig(Domain string) ([]ImageInfo, error) {\n\timagesPath := fmt.Sprintf(\"%s/%s/images.json\", PRODUCT_CONFIG_PATH, Domain)\n\tif !public.FileExists(imagesPath) {\n\t\tos.WriteFile(imagesPath, []byte(\"[]\"), 0644)\n\t}\n\tdata, err := os.ReadFile(imagesPath)\n\tif err != nil {\n\t\treturn []ImageInfo{}, fmt.Errorf(\"error reading images configuration file: %v\", err)\n\t}\n","sourceCodeStart":878,"sourceCodeEnd":914,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L878-L914","documentation":"SaveImagesConfig serializes a []ImageInfo slice with json.MarshalIndent and writes it to PRODUCT_CONFIG_PATH/<Domain>/images.json. This error is returned when json.MarshalIndent fails on the images slice. Like error 197, this only happens if ImageInfo (or its fields) contains values json cannot encode — unsupported types, cyclic references, or channels/funcs — since a plain slice of structs marshals fine.","triggerScenarios":"Calling SaveImagesConfig(domain, images) where an ImageInfo field (or nested struct) is a func, channel, or contains a reference cycle, causing json.MarshalIndent to return 'json: unsupported type'.","commonSituations":"A developer extended ImageInfo with a non-serializable field (handler, connection, sync.Mutex used carelessly in a cycle) without a json:\"-\" tag or custom MarshalJSON, then saved the images config.","solutions":["Read the wrapped 'json: unsupported type: ...' message to identify the failing type","Add json:\"-\" to non-serializable ImageInfo fields or implement MarshalJSON on them","Replace unsupported fields with serializable equivalents (string IDs, plain structs)","Guard against cycles by storing indices/IDs instead of self-referencing pointers"],"exampleFix":"// before\ntype ImageInfo struct {\n\tURL    string     `json:\"url\"`\n\tLoader func()     // causes: json: unsupported type: func()\n}\n\n// after\ntype ImageInfo struct {\n\tURL    string     `json:\"url\"`\n\tLoader func()     `json:\"-\"`\n}","handlingStrategy":"validation","validationCode":"// validate images slice is serializable before SaveImagesConfig\nfunc imagesAreSerializable(images []ImageInfo) error {\n\t_, err := json.Marshal(images)\n\treturn err\n}\n// usage:\nif err := imagesAreSerializable(images); err != nil {\n\tlog.Printf(\"images not serializable, fix ImageInfo fields: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := SaveImagesConfig(domain, images); err != nil {\n\tif strings.Contains(err.Error(), \"marshalling images\") {\n\t\tlog.Printf(\"ImageInfo contains unsupported field type: %v\", err)\n\t\t// sanitize: strip non-serializable fields and retry\n\t\treturn err\n\t}\n\treturn err\n}","preventionTips":["Tag non-serializable ImageInfo fields with json:\"-\"","Add a unit test marshalling a representative []ImageInfo","Keep ImageInfo free of funcs, channels, and cyclic pointers","Prefer storing plain data (URLs, IDs, dimensions) in persisted image records"],"tags":["json","marshal","go","images"],"backgroundTag":"json-marshal-unsupported-type","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"}