Billionmail/BillionMail · warning

error marshalling empty site map: %v

Error message

error marshalling empty site map: %v

What it means

When PRODUCT_CONFIG_PATH/<domain>/sitemap.json does not exist, GetSiteMap self-heals by marshalling an empty []SiteMap and writing it to the file. This error wraps a json.MarshalIndent failure on that empty slice. Marshalling an empty slice of plain structs cannot fail, so this is a defensive branch that only fires if SiteMap gains unmarshalable fields (chan, func, cycles).

Source

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

		return fmt.Errorf("error saving style config file: %v", err)
	}
	return nil
}

// 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

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Inspect the wrapped message ('json: unsupported type: ...') to identify the offending SiteMap field.
  2. Remove/replace the unmarshalable field in SiteMap with a serializable type.
  3. As a belt-and-braces measure, replace the marshal-and-save fallback with writing a literal []byte("[]\n") constant, which can never fail marshalling.

Example fix

// before
emptySiteMapJson, err := json.MarshalIndent(emptySiteMap, "", "  ")
if err != nil { return nil, fmt.Errorf("error marshalling empty site map: %v", err) }
// after
emptySiteMapJson := []byte("[]\n") // an empty JSON array is always valid; no marshal step to fail
Defensive patterns

Strategy: fallback

Validate before calling

// Not triggerable at runtime with current SiteMap; add a regression test:
b, err := json.Marshal([]SiteMap{})
if err != nil || string(b) != "[]" { t.Fatalf("empty SiteMap must marshal to []") }

Type guard

func isMarshalable(v any) bool {
    _, err := json.Marshal(v)
    return err == nil
}
// usage: if !isMarshalable(emptySiteMap) { ... }

Try / catch

m, err := GetSiteMap(domain)
if err != nil {
    if strings.Contains(err.Error(), "error marshalling empty site map") {
        // struct defect: do not retry, fix SiteMap definition
    }
    return err
}

Prevention

When it happens

Trigger: First access to the sitemap for a domain with no sitemap.json, combined with a SiteMap type that contains a value json cannot encode (only possible after type changes).

Common situations: Hit only by developers modifying SiteMap — e.g. adding a callback field or a cyclic pointer — then calling GetSiteMap/GetSitemapPrompt/AddSiteMapNode on a fresh domain.

Related errors


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