Billionmail/BillionMail · error

error reading images configuration file

Error message

error reading images configuration file: %v

What it means

ReadImagesConfig returns this when os.ReadFile fails on PRODUCT_CONFIG_PATH/<Domain>/images.json. Note the code pre-creates the file with '[]' if missing, so a read failure usually means the file exists but is unreadable (permissions, path mismatch), or the WriteFile seeding call failed silently and the file is still absent.

Solutions

  1. Check that PRODUCT_CONFIG_PATH/<Domain>/images.json exists and is readable by the service user (ls -l).
  2. Fix the seeding code to create the directory and check its error: os.MkdirAll + handle the WriteFile error instead of ignoring it.
  3. Verify PRODUCT_CONFIG_PATH is the intended path in the running environment.
  4. If race-prone, tolerate os.IsNotExist by returning an empty slice instead of an error.

Example fix

// before
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)
}
// after
dir := filepath.Dir(imagesPath)
if err := os.MkdirAll(dir, 0755); err != nil {
	return []ImageInfo{}, fmt.Errorf("error creating images dir: %v", err)
}
if !public.FileExists(imagesPath) {
	if err := os.WriteFile(imagesPath, []byte("[]"), 0644); err != nil {
		return []ImageInfo{}, fmt.Errorf("error seeding images file: %v", err)
	}
}
data, err := os.ReadFile(imagesPath)
if os.IsNotExist(err) {
	return []ImageInfo{}, nil
}
if err != nil {
	return []ImageInfo{}, fmt.Errorf("error reading images configuration file: %v", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

p := filepath.Join(PRODUCT_CONFIG_PATH, Domain, "images.json")
if fi, err := os.Stat(p); err != nil {
	return fmt.Errorf("images.json missing: %s", p)
} else if fi.IsDir() {
	return fmt.Errorf("images.json path is a directory")
}
if f, err := os.Open(p); err != nil {
	return fmt.Errorf("images.json unreadable: %v", err)
} else {
	f.Close()
}

Try / catch

images, err := ReadImagesConfig(domain)
if err != nil && strings.Contains(err.Error(), "reading images configuration file") {
	images = []ImageInfo{} // treat unreadable file as empty
}

Prevention

When it happens

Trigger: Calling ReadImagesConfig (directly or via UploadImage/ModifyImage/RemoveImage) when the seeded os.WriteFile([]byte("[]")) in the existence check fails (directory missing, permission denied) and then os.ReadFile errors.

Common situations: PRODUCT_CONFIG_PATH misconfigured after redeploy; config dir mounted read-only or with wrong ownership; the seeding write failing because the directory doesn't exist.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

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

	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)
	}
	return images, nil
}

type UploadImageResponse struct {
	Status bool   `json:"status"`
	Msg    string `json:"msg"`
	Data   struct {
		URL string `json:"url"`
	} `json:"data"`
}

View on GitHub (pinned to fc36c76c05)