Billionmail/BillionMail · error

error saving images file: %v

Error message

error saving images file: %v

What it means

SaveImagesConfig wraps any failure of os.WriteFile when persisting the domain's images.json (under PRODUCT_CONFIG_PATH/<Domain>/). The library throws it whenever the file cannot be marshalled-then-written, typically due to filesystem/permission problems, and it surfaces the underlying OS error via %v.

Source

Thrown at core/internal/service/askai/project.go:900

	HeadingFont         string `json:"heading_font"`
	LinkFooterColor     string `json:"link_footer_color"`
	LinkSocialColor     string `json:"link_social_color"`
	PageBackground      string `json:"page_background"`
	TextColor           string `json:"text_color"`
}

func SaveImagesConfig(Domain string, images []ImageInfo) error {
	imagesPath := fmt.Sprintf("%s/%s/images.json", PRODUCT_CONFIG_PATH, Domain)
	if !public.FileExists(PRODUCT_CONFIG_PATH + "/" + Domain) {
		os.MkdirAll(PRODUCT_CONFIG_PATH+"/"+Domain, os.ModePerm)
	}
	data, err := json.MarshalIndent(images, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling images: %v", err)
	}
	err = os.WriteFile(imagesPath, data, 0644)
	if err != nil {
		return fmt.Errorf("error saving images file: %v", err)
	}
	return nil
}

func ReadImagesConfig(Domain string) ([]ImageInfo, error) {
	imagesPath := fmt.Sprintf("%s/%s/images.json", PRODUCT_CONFIG_PATH, Domain)
	if !public.FileExists(imagesPath) {
		os.WriteFile(imagesPath, []byte("[]"), 0644)
	}
	data, err := os.ReadFile(imagesPath)
	if err != nil {
		return []ImageInfo{}, fmt.Errorf("error reading images configuration file: %v", err)
	}

	var images []ImageInfo
	err = json.Unmarshal(data, &images)
	if err != nil {
		return []ImageInfo{}, fmt.Errorf("error unmarshalling images configuration: %v", err)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Ensure the target directory exists before writing: os.MkdirAll(fmt.Sprintf("%s/%s", PRODUCT_CONFIG_PATH, Domain), 0755) at the top of SaveImagesConfig.
  2. Check permissions/ownership of PRODUCT_CONFIG_PATH and images.json (chown/chmod to the service user).
  3. Verify the volume is writable and not full (df -h, mount flags).
  4. Log the full path from the wrapped error to confirm PRODUCT_CONFIG_PATH points where you expect.

Example fix

// before
func SaveImagesConfig(Domain string, images []ImageInfo) error {
	data, err := json.MarshalIndent(images, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling images: %v", err)
	}
	err = os.WriteFile(imagesPath, data, 0644)
	if err != nil {
		return fmt.Errorf("error saving images file: %v", err)
	}
// after
func SaveImagesConfig(Domain string, images []ImageInfo) error {
	if err := os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, Domain), 0755); err != nil {
		return fmt.Errorf("error creating images dir: %v", err)
	}
	data, err := json.MarshalIndent(images, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling images: %v", err)
	}
	tmp := imagesPath + ".tmp"
	if err := os.WriteFile(tmp, data, 0644); err != nil {
		return fmt.Errorf("error saving images file: %v", err)
	}
	return os.Rename(tmp, imagesPath)
}
Defensive patterns

Strategy: validation

Validate before calling

dir := filepath.Join(PRODUCT_CONFIG_PATH, Domain)
if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
	return fmt.Errorf("images dir %s missing", dir)
}
if f, err := os.OpenFile(filepath.Join(dir, "images.json"), os.O_WRONLY|os.O_CREATE, 0644); err != nil {
	return fmt.Errorf("images.json not writable: %v", err)
} else {
	f.Close()
}

Type guard

func canWrite(path string) bool {
	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return false
	}
	_ = f.Close()
	return true
}

Try / catch

if err := SaveImagesConfig(domain, images); err != nil {
	if strings.Contains(err.Error(), "error saving images file") {
		// handle write failure: check disk/permissions, retry or queue
	}
}

Prevention

When it happens

Trigger: Calling SaveImagesConfig (directly or via UploadImage/ModifyImage/RemoveImage flows) when the PRODUCT_CONFIG_PATH/<Domain> directory does not exist, the process lacks write permission on images.json, or the disk is full.

Common situations: Deployments where the config directory was never created before first save; running the service as a non-root user after files were created by root; read-only container filesystems; full disk on the host.

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


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/36afb7c6dbcddffa. Report an issue: GitHub.