{"record":{"id":"f7ce62812fc3ebba","repo":"Billionmail/BillionMail","slug":"error-getting-prompt-config-v","errorCode":null,"errorMessage":"error getting prompt config: %v","messagePattern":"error getting prompt config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":818,"sourceCode":"\tif err != nil {\n\t\treturn PromptConfig{}, fmt.Errorf(\"error reading prompt config file: %v\", err)\n\t}\n\n\tvar config PromptConfig\n\terr = json.Unmarshal(data, &config)\n\tif err != nil {\n\t\treturn PromptConfig{}, fmt.Errorf(\"error unmarshalling prompt config: %v\", err)\n\t}\n\treturn config, nil\n}\n\n// ModifyPrompt updates the prompt configuration for a given domain.\n// It reads the existing prompt configuration, modifies the specified fields, and saves the updated configuration back to the file.\n// If the prompt is empty, it will not be updated.\nfunc ModifyPrompt(Domain string, Prompt string) error {\n\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","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L800-L836","documentation":"ModifyPrompt calls GetPrompt first to load the existing PromptConfig for the domain; this wrapper re-surfaces any GetPrompt failure (missing-and-uncreatable file, unreadable file, or corrupt JSON) prefixed with 'error getting prompt config'. It means the update could not even start because the current config could not be loaded.","triggerScenarios":"Calling ModifyPrompt(domain, newPrompt) when the domain's prompt_config.json is unreadable (permissions) or contains invalid JSON (error 195), or PRODUCT_CONFIG_PATH/<Domain> cannot be created on first use.","commonSituations":"Corrupt config from an earlier failed write; wrong PRODUCT_CONFIG_PATH configured for the deployment (config dir not mounted in Docker); file owned by root while the app runs as another user.","solutions":["Inspect the wrapped inner message to identify the real cause (read vs unmarshal vs save)","Fix the underlying prompt_config.json (validate with jq) or delete it so GetPrompt regenerates the default","Verify PRODUCT_CONFIG_PATH exists and is writable by the process user (check volume mounts in Docker)","Run as a user with read/write access to the config directory"],"exampleFix":"// before\n$ docker volume not mounted\nerror getting prompt config: error reading prompt config file: open /app/config/mydomain.com/prompt_config.json: no such file or directory\n\n// after\n# mount the config volume\nvolumes:\n  - ./config:/app/config","handlingStrategy":"try-catch","validationCode":"// ensure the config dir exists and is writable before ModifyPrompt\ndir := filepath.Join(PRODUCT_CONFIG_PATH, domain)\nif err := os.MkdirAll(dir, os.ModePerm); err != nil {\n\treturn fmt.Errorf(\"config dir unavailable: %w\", err)\n}\nif f, err := os.CreateTemp(dir, \".wtest\"); err != nil {\n\treturn fmt.Errorf(\"config dir not writable: %w\", err)\n} else {\n\tf.Close(); os.Remove(f.Name())\n}","typeGuard":null,"tryCatchPattern":"if err := ModifyPrompt(domain, prompt); err != nil {\n\tvar inner error\n\tif strings.Contains(err.Error(), \"unmarshalling\") {\n\t\t// recover from corrupt config by regenerating defaults\n\t\tos.Remove(filepath.Join(PRODUCT_CONFIG_PATH, domain, \"prompt_config.json\"))\n\t\tinner = ModifyPrompt(domain, prompt)\n\t} else {\n\t\tinner = err\n\t}\n\tif inner != nil {\n\t\tlog.Printf(\"ModifyPrompt failed for %s: %v\", domain, inner)\n\t}\n}","preventionTips":["Mount a writable volume at PRODUCT_CONFIG_PATH in containers","Run the app as a user that owns the config directory","Monitor config-dir permissions after deployment changes","Regenerate default config files when they fail to parse instead of failing hard"],"tags":["json","config","io","go"],"backgroundTag":"json-unmarshal-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"}