{"record":{"id":"5cf52034cf75ab19","repo":"Billionmail/BillionMail","slug":"error-saving-prompt-config-file-v","errorCode":null,"errorMessage":"error saving prompt config file: %v","messagePattern":"error saving prompt config file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":834,"sourceCode":"\tconfig, err := GetPrompt(Domain)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting prompt config: %v\", err)\n\t}\n\n\t// Update the prompt configuration\n\tif Prompt != \"\" {\n\t\tconfig.Prompt = Prompt\n\t}\n\n\tconfig.UpdateTime = public.GetNowTime()\n\tfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/prompt_config.json\", Domain)\n\tdata, err := json.Marshal(config)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling prompt config: %v\", err)\n\t}\n\terr = os.WriteFile(filename, data, 0644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error saving prompt config file: %v\", err)\n\t}\n\treturn nil\n}\n\ntype BotResponse struct {\n\tData   BotData `json:\"data\"`\n\tMsg    string  `json:\"msg\"`\n\tStatus bool    `json:\"status\"`\n}\n\ntype BotData struct {\n\tDescription   string       `json:\"description\"`\n\tDomain        string       `json:\"domain\"`\n\tMarkdown      string       `json:\"markdown\"`\n\tFooter        BotFooter    `json:\"footer\"`\n\tIcon          string       `json:\"icon\"`\n\tImages        []ImageInfo  `json:\"images\"`\n\tKeywords      string       `json:\"keywords\"`","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L816-L852","documentation":"After successfully marshalling the config, ModifyPrompt writes it with os.WriteFile to PRODUCT_CONFIG_PATH/<Domain>/prompt_config.json. This error wraps the os.WriteFile failure — the file could not be created or written (missing directory, permissions, read-only filesystem, disk full). The config update is not saved.","triggerScenarios":"Calling ModifyPrompt(domain, prompt) when PRODUCT_CONFIG_PATH/<Domain>/ does not exist (GetPrompt only creates it when the file is absent, but a stale half-deleted dir can break this), the directory is read-only, or the disk is full.","commonSituations":"Docker container with a read-only root filesystem and no writable volume mounted at the config path; config dir owned by root while app runs unprivileged; disk quota exceeded; parent directory removed concurrently.","solutions":["Verify the domain directory PRODUCT_CONFIG_PATH/<Domain> exists and create it: os.MkdirAll(path, os.ModePerm) before WriteFile","Check directory permissions (ls -ld) and chown/chmod so the process user can write","Ensure the volume/filesystem is writable (not read-only mount, not out of disk: df -h)","Make the save atomic: write to a temp file in the same dir then os.Rename over prompt_config.json"],"exampleFix":"// before\nfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/prompt_config.json\", Domain)\ndata, err := json.Marshal(config)\nif err != nil { return fmt.Errorf(\"error marshalling prompt config: %v\", err) }\nerr = os.WriteFile(filename, data, 0644)\n\n// after\nfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/prompt_config.json\", Domain)\nif err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {\n\treturn fmt.Errorf(\"error creating config dir: %v\", err)\n}\ndata, err := json.Marshal(config)\nif err != nil { return fmt.Errorf(\"error marshalling prompt config: %v\", err) }\nif err := os.WriteFile(filename, data, 0644); err != nil {\n\treturn fmt.Errorf(\"error saving prompt config file: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: verify the target path is writable before ModifyPrompt\nfunc configPathWritable(domain string) bool {\n\tdir := filepath.Join(PRODUCT_CONFIG_PATH, domain)\n\tif err := os.MkdirAll(dir, os.ModePerm); err != nil {\n\t\treturn false\n\t}\n\tprobe := filepath.Join(dir, \".write_probe\")\n\tif err := os.WriteFile(probe, nil, 0644); err != nil {\n\t\treturn false\n\t}\n\tos.Remove(probe)\n\treturn true\n}","typeGuard":null,"tryCatchPattern":"if err := ModifyPrompt(domain, prompt); err != nil {\n\tif strings.Contains(err.Error(), \"saving prompt config file\") {\n\t\t// fs issue: check disk space and permissions before retry\n\t\tlog.Printf(\"config save failed (check mount/permissions/disk): %v\", err)\n\t\treturn fmt.Errorf(\"config storage unavailable: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Always MkdirAll the target directory before os.WriteFile","Verify volume mounts are writable in read-only container deployments","Monitor disk space/quota on the config volume","Use atomic writes (temp file + rename) and check the wrapped syscall (EACCES, ENOSPC, EROFS) in logs"],"tags":["filesystem","io","permissions","go"],"backgroundTag":"file-write-permission-denied","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"}