Billionmail/BillionMail · error

error reading company profile file: %v

Error message

error reading company profile file: %v

What it means

When company_profile.json exists, ReadCompanyProfile loads it with os.ReadFile. This error wraps a read failure on an existing file — the file passed the FileExists check but could not actually be read, usually due to permissions, the path being a directory, or a TOCTOU deletion between the check and the read.

Source

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

		// If the company profile file does not exist, return a default profile
		// This allows the system to handle cases where the profile has not been set up yet
		// and avoids errors when trying to read a non-existent file.
		// It also allows the user to create a new profile without needing to handle file not
		// found errors.
		companyProfileDefault.UpdateTime = public.GetNowTime()
		companyProfileJson, err := json.MarshalIndent(companyProfileDefault, "", "  ")
		if err != nil {
			return CompanyProfile{}, fmt.Errorf("error marshalling company profile: %v", err)
		}
		err = os.WriteFile(filename, companyProfileJson, 0644)
		if err != nil {
			return CompanyProfile{}, fmt.Errorf("error creating company profile file: %v", err)
		}
		return companyProfileDefault, nil
	}
	data, err := os.ReadFile(filename)
	if err != nil {
		return CompanyProfile{}, fmt.Errorf("error reading company profile file: %v", err)
	}
	var profile CompanyProfile
	err = json.Unmarshal(data, &profile)
	if err != nil {
		return CompanyProfile{}, fmt.Errorf("error unmarshalling company profile: %v", err)
	}
	return profile, nil
}

// SaveCompanyProfile saves the provided CompanyProfile to a JSON file based on the domain.
// It creates the directory if it does not exist and writes the profile information to company_profile.json
func SaveCompanyProfile(Domain string, profile CompanyProfile) error {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/company_profile.json", Domain)
	data, err := json.Marshal(profile)
	if err != nil {
		return fmt.Errorf("error marshalling company profile: %v", err)
	}
	err = os.WriteFile(filename, data, 0644)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the wrapped %v error for the OS cause (EACCES, EISDIR, ENOENT)
  2. Fix file permissions/ownership so the service user can read company_profile.json
  3. If a directory occupies the name, remove it and let the lazy-create path regenerate the default
  4. Handle the race by attempting ReadFile first and treating os.IsNotExist as 'create default' instead of pre-checking FileExists

Example fix

// before
if !public.FileExists(filename) { /* create default */ }
data, err := os.ReadFile(filename)
// after
data, err := os.ReadFile(filename)
if os.IsNotExist(err) { /* create default */ } else if err != nil {
    return CompanyProfile{}, fmt.Errorf("error reading company profile file: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

p := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/company_profile.json", domain)
if fi, err := os.Stat(p); err == nil {
    if fi.IsDir() {
        return fmt.Errorf("%s is a directory", p)
    }
    f, err := os.OpenFile(p, os.O_RDONLY, 0)
    if err != nil {
        return fmt.Errorf("profile unreadable: %w", err)
    }
    f.Close()
}

Try / catch

profile, err := ReadCompanyProfile(domain)
if err != nil && strings.Contains(err.Error(), "reading company profile file") {
    if os.IsPermission(errors.Unwrap(err)) {
        log.Errorf("Permission denied on %s profile", domain)
    }
    // fall back to an empty default profile
    profile = CompanyProfile{}
}

Prevention

When it happens

Trigger: os.ReadFile(filename) fails though FileExists returned true: permission denied on the file; company_profile.json is a directory; file deleted concurrently between FileExists and ReadFile; I/O error on the underlying storage.

Common situations: Profile file created by root, service running as unprivileged user; a directory accidentally named company_profile.json; concurrent initialization racing to create/read the profile; corrupted volume.

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/14ce2b2c533af98e. Report an issue: GitHub.