{"record":{"id":"b8bcb78dd5faa205","repo":"Billionmail/BillionMail","slug":"error-marshalling-project-configuration-v","errorCode":null,"errorMessage":"error marshalling project configuration: %v","messagePattern":"error marshalling project configuration: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":134,"sourceCode":"\t\treturn ProjectConfig{}, fmt.Errorf(\"error unmarshalling project configuration: %v\", err)\n\t}\n\treturn config, nil\n}\n\n// SaveProjectConfig saves the provided ProjectConfig to a JSON file based on the domain.\n// It creates the directory if it does not exist and writes the configuration to project.json.\n// If the file cannot be written, it returns an error.\n// If the directory does not exist, it creates it with appropriate permissions.\nfunc SaveProjectConfig(Domain string, config ProjectConfig) error {\n\tprojectConfigPath := fmt.Sprintf(\"%s/%s\", PRODUCT_CONFIG_PATH, Domain)\n\tif !public.FileExists(projectConfigPath) {\n\t\tos.MkdirAll(projectConfigPath, os.ModePerm)\n\t}\n\tfilename := fmt.Sprintf(\"%s/project.json\", projectConfigPath)\n\n\tconfigStr, err := json.MarshalIndent(config, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling project configuration: %v\", err)\n\t}\n\tconfig.UpdateTime = public.GetNowTime() // Update the time before saving\n\terr = os.WriteFile(filename, configStr, os.ModePerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error writing project configuration file: %v\", err)\n\t}\n\treturn nil\n}\n\n// Create initializes a new project configuration with the provided domain and URLs.\n// It sets default values for other fields and saves the configuration to a file.\nfunc Create(Domain string, urls []string) error {\n\turlsCount := len(urls)\n\tif urls == nil || urlsCount == 0 {\n\t\turls = append(urls, \"http://\"+Domain) // Default URL if none provided\n\t}\n\tif urlsCount > 3 {\n\t\treturn fmt.Errorf(\"Add up to 3 URLs\")","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L116-L152","documentation":"SaveProjectConfig serializes the ProjectConfig to indented JSON with json.MarshalIndent before writing to disk; if marshalling fails (unsupported field types like channels, funcs, or invalid maps), it returns this wrapped error and nothing is written.","triggerScenarios":"SaveProjectConfig (via Create, SetProjectStatus, ModifyBaseInfo, AppendProjectConfig) receives a ProjectConfig containing values json cannot encode — e.g. a field added with an unsupported type (chan, func, complex), a map with non-string keys, or a circular structure.","commonSituations":"A developer extends ProjectConfig with a new field of a non-JSON-serializable type; embedding a struct containing sync primitives; custom types without MarshalJSON handling malformed data.","solutions":["Review recently added ProjectConfig fields; tag unsupported ones with `json:\"-\"` or change their types","Implement MarshalJSON for any custom field type that fails to encode","Use the wrapped %v cause to identify which value json cannot handle","Add a unit test marshalling a fully-populated ProjectConfig to catch this at build time"],"exampleFix":"// before\ntype ProjectConfig struct {\n    Callback func() `json:\"callback\"` // unsupported\n}\n// after\ntype ProjectConfig struct {\n    Callback func() `json:\"-\"` // excluded from JSON\n}","handlingStrategy":"validation","validationCode":"if _, err := json.MarshalIndent(config, \"\", \"  \"); err != nil {\n    return fmt.Errorf(\"config not JSON-serializable: %v\", err)\n} // run before calling SaveProjectConfig","typeGuard":null,"tryCatchPattern":"if err := askai.SaveProjectConfig(domain, cfg); err != nil {\n    if strings.Contains(err.Error(), \"marshalling\") {\n        log.Errorf(\"ProjectConfig has non-serializable fields: %v\", err)\n        return err\n    }\n    return err\n}","preventionTips":["Keep ProjectConfig fields limited to JSON-safe types; use `json:\"-\"` for runtime-only fields","Add a round-trip test (marshal then unmarshal) for ProjectConfig in CI","Give custom types a MarshalJSON method if they must be serialized","Avoid embedding structs with channels, funcs, or sync primitives"],"tags":["go","json","config","serialization"],"backgroundTag":"json-marshal-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"}