Billionmail/BillionMail · error

error saving empty site map file: %v

Error message

error saving empty site map file: %v

What it means

In the same self-healing branch of GetSiteMap, after marshalling an empty []SiteMap the function attempts os.WriteFile(filename, ..., 0644) to bootstrap sitemap.json. This error wraps the write failure — most commonly because the directory PRODUCT_CONFIG_PATH/<domain>/ does not exist, since GetSiteMap (like SaveStyleConfig) never creates it.

Source

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

// GetSiteMap retrieves the site map for a given domain from a JSON file.
// It reads the site map file and returns a slice of SiteMap structs.
// If the file does not exist or cannot be read, it returns an error.
func GetSiteMap(Domain string) ([]SiteMap, error) {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/sitemap.json", Domain)
	if !public.FileExists(filename) {
		// If the site map file does not exist, return an empty slice
		// This allows the system to handle cases where the site map has not been set up
		// and avoids errors when trying to read a non-existent file.
		// It also allows the user to create a new site map without needing to handle file not found errors.
		emptySiteMap := []SiteMap{}
		emptySiteMapJson, err := json.MarshalIndent(emptySiteMap, "", "  ")
		if err != nil {
			return nil, fmt.Errorf("error marshalling empty site map: %v", err)
		}
		err = os.WriteFile(filename, emptySiteMapJson, 0644)
		if err != nil {
			return nil, fmt.Errorf("error saving empty site map file: %v", err)
		}
		return emptySiteMap, nil
	}
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("error reading site map file: %v", err)
	}

	var siteMap []SiteMap
	err = json.Unmarshal(data, &siteMap)
	if err != nil {
		return nil, fmt.Errorf("error unmarshalling site map: %v", err)
	}
	return siteMap, nil
}

func SaveSiteMap(Domain string, siteMap []SiteMap) error {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/sitemap.json", Domain)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Create the domain config directory before writing: os.MkdirAll(filepath.Dir(filename), 0755) in GetSiteMap's bootstrap branch.
  2. Check permissions/ownership of PRODUCT_CONFIG_PATH/<domain>/ for the service user (chown/chmod).
  3. Confirm PRODUCT_CONFIG_PATH points to an existing, writable base directory.
  4. Read the nested os error to confirm the cause (e.g. 'no such file or directory' vs 'permission denied').

Example fix

// before
err = os.WriteFile(filename, emptySiteMapJson, 0644)
if err != nil { return nil, fmt.Errorf("error saving empty site map file: %v", err) }
// after
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
    return nil, fmt.Errorf("error creating sitemap dir: %v", err)
}
err = os.WriteFile(filename, emptySiteMapJson, 0644)
if err != nil { return nil, fmt.Errorf("error saving empty site map file: %v", err) }
Defensive patterns

Strategy: retry

Validate before calling

func ensureSitemapWritable(domain string) error {
    dir := filepath.Join(PRODUCT_CONFIG_PATH, domain)
    if err := os.MkdirAll(dir, 0755); err != nil { return err }
    f, err := os.CreateTemp(dir, ".wtest*")
    if err != nil { return err }
    f.Close(); os.Remove(f.Name())
    return nil
}
// call before GetSiteMap on fresh domains

Try / catch

m, err := GetSiteMap(domain)
if err != nil && strings.Contains(err.Error(), "error saving empty site map file") {
    // create the dir, then retry once
    os.MkdirAll(filepath.Join(PRODUCT_CONFIG_PATH, domain), 0755)
    m, err = GetSiteMap(domain)
}

Prevention

When it happens

Trigger: First call to GetSiteMap (or callers GetSitemap, GetSitemapPrompt, AddSiteMapNode, RemoveSiteMapNode, AppendSitemap) for a domain with no sitemap.json and no PRODUCT_CONFIG_PATH/<domain>/ directory, or with a read-only/non-writable directory.

Common situations: Newly registered domain whose config folder was never created; service user lacks write permission; container volume mounted read-only; PRODUCT_CONFIG_PATH misconfigured to a non-existent base 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


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