{"record":{"id":"e1ac583a7e9b139e","repo":"Billionmail/BillionMail","slug":"error-writing-project-configuration-file-v","errorCode":null,"errorMessage":"error writing project configuration file: %v","messagePattern":"error writing project configuration file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"core/internal/service/askai/project.go","lineNumber":139,"sourceCode":"// 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\")\n\t}\n\t// Ensure all URLs start with \"http://\"\n\tfor i, urlStr := range urls {\n\t\tif !strings.HasPrefix(urlStr, \"http\") {\n\t\t\turls[i] = \"http://\" + urlStr","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L121-L157","documentation":"After successful marshalling, SaveProjectConfig writes the bytes to PRODUCT_CONFIG_PATH/<Domain>/project.json via os.WriteFile; any filesystem write failure is wrapped as this error, meaning the config was NOT persisted.","triggerScenarios":"os.WriteFile fails because the parent directory does not exist (MkdirAll skipped/failed), permission denied, disk full, or read-only filesystem.","commonSituations":"Docker volume mounted read-only; disk quota/full disk on the config volume; MkdirAll failed earlier but its error was ignored; running container as non-root user without write access to PRODUCT_CONFIG_PATH.","solutions":["Check disk space (df -h) and volume writability for PRODUCT_CONFIG_PATH","Verify the process user has write permission on the project directory","Ensure the os.MkdirAll error is checked before WriteFile — don't ignore it","In deployment, mount the config directory as read-write and correct ownership"],"exampleFix":"// before\nos.MkdirAll(projectConfigPath, os.ModePerm) // error ignored\n// after\nif err := os.MkdirAll(projectConfigPath, os.ModePerm); err != nil {\n    return fmt.Errorf(\"error creating project config directory: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"if err := unix.Access(projectConfigPath, unix.W_OK); err != nil {\n    return fmt.Errorf(\"config path not writable: %v\", err)\n}\nif _, err := os.Stat(projectConfigPath); os.IsNotExist(err) {\n    os.MkdirAll(projectConfigPath, 0o755)\n}","typeGuard":null,"tryCatchPattern":"if err := askai.SaveProjectConfig(domain, cfg); err != nil {\n    if strings.Contains(err.Error(), \"writing\") {\n        // check disk/permissions before surfacing\n        return fmt.Errorf(\"persisting project config failed (check disk space & permissions): %w\", err)\n    }\n    return err\n}","preventionTips":["Mount the config directory read-write and set correct ownership in Docker","Monitor disk space/quota on the config volume with alerts","Always check the MkdirAll error before os.WriteFile","Prefer atomic write (temp file + rename) and set explicit file modes instead of os.ModePerm (0777)"],"tags":["go","filesystem","io","permissions","persistence"],"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-14T00:17:10.932Z"}