Billionmail/BillionMail · critical
error writing project configuration file: %v
Error message
error writing project configuration file: %v
What it means
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.
Source
Thrown at core/internal/service/askai/project.go:139
// SaveProjectConfig saves the provided ProjectConfig to a JSON file based on the domain.
// It creates the directory if it does not exist and writes the configuration to project.json.
// If the file cannot be written, it returns an error.
// If the directory does not exist, it creates it with appropriate permissions.
func SaveProjectConfig(Domain string, config ProjectConfig) error {
projectConfigPath := fmt.Sprintf("%s/%s", PRODUCT_CONFIG_PATH, Domain)
if !public.FileExists(projectConfigPath) {
os.MkdirAll(projectConfigPath, os.ModePerm)
}
filename := fmt.Sprintf("%s/project.json", projectConfigPath)
configStr, err := json.MarshalIndent(config, "", " ")
if err != nil {
return fmt.Errorf("error marshalling project configuration: %v", err)
}
config.UpdateTime = public.GetNowTime() // Update the time before saving
err = os.WriteFile(filename, configStr, os.ModePerm)
if err != nil {
return fmt.Errorf("error writing project configuration file: %v", err)
}
return nil
}
// Create initializes a new project configuration with the provided domain and URLs.
// It sets default values for other fields and saves the configuration to a file.
func Create(Domain string, urls []string) error {
urlsCount := len(urls)
if urls == nil || urlsCount == 0 {
urls = append(urls, "http://"+Domain) // Default URL if none provided
}
if urlsCount > 3 {
return fmt.Errorf("Add up to 3 URLs")
}
// Ensure all URLs start with "http://"
for i, urlStr := range urls {
if !strings.HasPrefix(urlStr, "http") {
urls[i] = "http://" + urlStrView on GitHub (pinned to fc36c76c05)
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
Example fix
// before
os.MkdirAll(projectConfigPath, os.ModePerm) // error ignored
// after
if err := os.MkdirAll(projectConfigPath, os.ModePerm); err != nil {
return fmt.Errorf("error creating project config directory: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if err := unix.Access(projectConfigPath, unix.W_OK); err != nil {
return fmt.Errorf("config path not writable: %v", err)
}
if _, err := os.Stat(projectConfigPath); os.IsNotExist(err) {
os.MkdirAll(projectConfigPath, 0o755)
} Try / catch
if err := askai.SaveProjectConfig(domain, cfg); err != nil {
if strings.Contains(err.Error(), "writing") {
// check disk/permissions before surfacing
return fmt.Errorf("persisting project config failed (check disk space & permissions): %w", err)
}
return err
} Prevention
- 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)
When it happens
Trigger: os.WriteFile fails because the parent directory does not exist (MkdirAll skipped/failed), permission denied, disk full, or read-only filesystem.
Common situations: 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.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- error reading project configuration file: %v
- error writing knowledge base file: %v
- error saving knowledge base: %v
- error creating company profile file: %v
- error reading company profile file: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/e1ac583a7e9b139e.
Report an issue: GitHub.