Billionmail/BillionMail · error
error marshalling footer config: %v
Error message
error marshalling footer config: %v
What it means
ModifyFooter in core/internal/service/askai/project.go:764 fails when json.Marshal cannot serialize the updated FooterConfig struct before it is written to PRODUCT_CONFIG_PATH/<domain>/footer_config.json. Marshal errors are rare for plain config structs and almost always indicate an unsupported value (channel, func, cyclic pointer) embedded in the struct. The wrapped json.MarshalTypeError is returned so the caller can surface why serialization failed.
Source
Thrown at core/internal/service/askai/project.go:764
return fmt.Errorf("error getting footer config: %v", err)
}
// Update the footer configuration
if CopyrightText != "" {
config.CopyrightText = CopyrightText
}
if Disclaimer != "" {
config.Disclaimer = Disclaimer
}
if Text != "" {
config.Text = Text
}
config.UpdateTime = public.GetNowTime()
filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/footer_config.json", Domain)
data, err := json.Marshal(config)
if err != nil {
return fmt.Errorf("error marshalling footer config: %v", err)
}
err = os.WriteFile(filename, data, 0644)
if err != nil {
return fmt.Errorf("error saving footer config file: %v", err)
}
return nil
}
// GetPrompt retrieves the prompt configuration for a given domain from a JSON file.
// It reads the prompt configuration file and returns a PromptConfig struct.
// If the file does not exist or cannot be read, it returns an error.
func GetPrompt(Domain string) (PromptConfig, error) {
filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/prompt_config.json", Domain)
if !public.FileExists(filename) {
// If the prompt config file does not exist, return a default prompt config
// This allows the system to handle cases where the prompt 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 prompt configuration without needing to handle fileView on GitHub (pinned to fc36c76c05)
Solutions
- Inspect the wrapped error (%v) for the json.MarshalTypeError field/type to identify the offending FooterConfig field.
- Remove or make serializable any func/chan/complex fields added to FooterConfig; encode them as strings or IDs.
- Check custom MarshalJSON implementations on FooterConfig and its field types for error paths.
- If a cycle is suspected, add pointer guards or store plain values instead of shared pointers.
Example fix
// before
type FooterConfig struct {
Text string
OnSave func() // unsupported by json.Marshal
}
// after
type FooterConfig struct {
Text string
OnSaveName string `json:"onSaveName"` // serializable representation
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: validate serializability before calling ModifyFooter
func footerConfigSerializable(domain string) error {
cfg, err := GetFooter(domain)
if err != nil {
return err
}
_, err = json.Marshal(cfg)
return err
} Type guard
func isMarshalable(v any) bool {
_, err := json.Marshal(v)
return err == nil
} Try / catch
if err := askai.ModifyFooter(domain, copyright, disclaimer, text); err != nil {
var mte *json.MarshalTypeError
if errors.As(err, &mte) {
log.Printf("footer config field %s (%s) not serializable", mte.Field, mte.Type)
} else {
log.Printf("modify footer failed: %v", err)
}
return err
} Prevention
- Keep FooterConfig limited to JSON-serializable field types (strings, numbers, bools).
- Add a unit test marshalling a representative FooterConfig instance.
- Avoid custom MarshalJSON implementations unless thoroughly tested.
- Never store funcs, channels, or shared cyclic pointers in config structs.
When it happens
Trigger: Calling ModifyFooter(domain, copyrightText, disclaimer, text) when the loaded FooterConfig (from GetFooter) contains a field json.Marshal cannot encode: an unsupported type (func, chan, complex), a custom MarshalJSON method that returns an error, or a cyclic pointer/graph (unsupported recursively).
Common situations: A developer extends FooterConfig with a non-serializable field (e.g., a callback or time.Time with a broken custom marshaler), or a custom MarshalJSON on FooterConfig/fields returns an error on the current data, or a data race corrupts pointer fields into a cycle between GetFooter and Marshal.
Related errors
- error marshalling project configuration: %v
- error marshalling default prompt config: %v
- error unmarshalling project configuration: %v
- error marshalling knowledge base: %v
- error marshalling company profile: %v
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/c6365e070e2026f1.
Report an issue: GitHub.