Billionmail/BillionMail · error

error marshalling default footer config: %v

Error message

error marshalling default footer config: %v

What it means

GetFooter auto-provisions a default FooterConfig (sample copyright text and disclaimer) when footer_config.json does not exist, serializing it with json.MarshalIndent before writing. This error wraps a failure of that default-config serialization. FooterConfig contains only plain string/time fields, so this wrapper practically fires only if the struct gains a field encoding/json cannot handle or a custom marshaler errors.

Source

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

// GetFooter retrieves the footer configuration for a given domain from a JSON file.
// It reads the footer configuration file and returns a FooterConfig struct.
// If the file does not exist or cannot be read, it returns an error.
func GetFooter(Domain string) (FooterConfig, error) {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/footer_config.json", Domain)
	if !public.FileExists(filename) {
		// If the footer config file does not exist, return a default footer config
		// This allows the system to handle cases where the footer configuration 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 footer configuration without needing to handle file
		// not found errors.
		defaultFooterConfig := FooterConfig{
			CopyrightText: "© 2023 Your Company. All rights reserved.",
			Disclaimer:    "This is a sample disclaimer.",
		}
		defaultFooterConfig.UpdateTime = public.GetNowTime()
		defaultFooterJson, err := json.MarshalIndent(defaultFooterConfig, "", "  ")
		if err != nil {
			return FooterConfig{}, fmt.Errorf("error marshalling default footer config: %v", err)
		}
		err = os.WriteFile(filename, defaultFooterJson, 0644)
		if err != nil {
			return FooterConfig{}, fmt.Errorf("error saving default footer config file: %v", err)
		}
		// Return the default footer config if the file does not exist
		return defaultFooterConfig, nil
	}
	data, err := os.ReadFile(filename)
	if err != nil {
		return FooterConfig{}, fmt.Errorf("error reading footer config file: %v", err)
	}

	var config FooterConfig
	err = json.Unmarshal(data, &config)
	if err != nil {
		return FooterConfig{}, fmt.Errorf("error unmarshalling footer config: %v", err)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Inspect the wrapped 'json: unsupported type' message to identify the offending FooterConfig field; remove it or tag it `json:"-"`.
  2. If FooterConfig has a custom MarshalJSON, fix the branch returning an error for default values.
  3. Add a test marshalling the default FooterConfig used for auto-provisioning.

Example fix

// before
type FooterConfig struct {
    Renderer func(w io.Writer) // unsupported
}
// after
type FooterConfig struct {
    Renderer func(w io.Writer) `json:"-"`
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(askai.FooterConfig{CopyrightText: "x", Disclaimer: "y"}); err != nil {
    return fmt.Errorf("FooterConfig not encodable: %w", err)
}

Type guard

func encodableConfig(c askai.FooterConfig) bool { _, err := json.Marshal(c); return err == nil }

Try / catch

cfg, err := askai.GetFooter(domain)
if err != nil {
    var ute *json.UnsupportedTypeError
    if errors.As(err, &ute) { /* FooterConfig schema bug — fix struct */ }
    return err
}

Prevention

When it happens

Trigger: Calling GetFooter(Domain) (directly, or via GetFooterPrompt/ModifyFooter/AutoGetProjectInfo) when footer_config.json is absent and the constructed defaultFooterConfig cannot be JSON-encoded.

Common situations: A developer adds a non-serializable field (func, chan) or a json.RawMessage populated with invalid data to FooterConfig; first-run provisioning of a new domain then fails on every call.

Related errors


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