Billionmail/BillionMail · error
error reading site map file: %v
Error message
error reading site map file: %v
What it means
GetSiteMap reads PRODUCT_CONFIG_PATH/<domain>/sitemap.json with os.ReadFile after confirming the file exists via public.FileExists. This error wraps any read failure — most often a TOCTOU race where the file is deleted between the existence check and the read, or permission problems on the file itself.
Source
Thrown at core/internal/service/askai/project.go:620
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)
data, err := json.MarshalIndent(siteMap, "", " ")
if err != nil {
return fmt.Errorf("error marshalling site map: %v", err)
}
err = os.WriteFile(filename, data, 0644)
if err != nil {View on GitHub (pinned to fc36c76c05)
Solutions
- Drop the FileExists pre-check and use os.ReadFile directly with errors.Is(err, os.ErrNotExist) to return an empty map instead — this removes the race window.
- Restore read permissions on sitemap.json for the service user (chmod 644, chown to service user).
- Verify the path is a regular file, not a directory: ls -la PRODUCT_CONFIG_PATH/<domain>/sitemap.json.
- Retry the read once on transient failure if a concurrent writer is expected.
Example fix
// before
if !public.FileExists(filename) { /* bootstrap empty */ }
data, err := os.ReadFile(filename)
if err != nil { return nil, fmt.Errorf("error reading site map file: %v", err) }
// after
data, err := os.ReadFile(filename)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return bootstrapEmptySiteMap(filename) // single code path, no TOCTOU race
}
return nil, fmt.Errorf("error reading site map file: %v", err)
} Defensive patterns
Strategy: fallback
Validate before calling
func readableFile(path string) bool {
f, err := os.Open(path)
if err != nil { return false }
f.Close()
return true
}
// check just before reading; but prefer removing the check-and-read race entirely Try / catch
m, err := GetSiteMap(domain)
if err != nil && strings.Contains(err.Error(), "error reading site map file") {
// transient (e.g. concurrent delete): fall back to empty map and re-bootstrap
return []SiteMap{}, nil
} Prevention
- Replace FileExists + ReadFile with a direct os.ReadFile and handle os.ErrNotExist — removes the TOCTOU race.
- Avoid external scripts deleting files under PRODUCT_CONFIG_PATH while the service runs.
- Restore uniform ownership/permissions (644 files, 755 dirs, service user) after migrations.
When it happens
Trigger: Concurrent deletion/removal of sitemap.json between FileExists and os.ReadFile (race with RemoveSiteMapNode or external cleanup); read permission removed from the file; filename resolves to a directory.
Common situations: Two requests hitting the sitemap concurrently (one recreating/normalizing the file while another reads); ops scripts pruning config files while the service runs; misconfigured permissions after a restore/migration.
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
- error saving empty site map file: %v
- error saving site map file: %v
- error reading footer config file: %v
- error reading prompt config file: %v
- error reading project configuration file: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/09f7019a841c1b1a.
Report an issue: GitHub.