Billionmail/BillionMail · error

error marshalling images: %v

Error message

error marshalling images: %v

What it means

SaveImagesConfig serializes a []ImageInfo slice with json.MarshalIndent and writes it to PRODUCT_CONFIG_PATH/<Domain>/images.json. This error is returned when json.MarshalIndent fails on the images slice. Like error 197, this only happens if ImageInfo (or its fields) contains values json cannot encode — unsupported types, cyclic references, or channels/funcs — since a plain slice of structs marshals fine.

Source

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

type BotStyle struct {
	AccentColor         string `json:"accent_color"`
	BodyFont            string `json:"body_font"`
	ContainerBackground string `json:"container_background"`
	HeadingFont         string `json:"heading_font"`
	LinkFooterColor     string `json:"link_footer_color"`
	LinkSocialColor     string `json:"link_social_color"`
	PageBackground      string `json:"page_background"`
	TextColor           string `json:"text_color"`
}

func SaveImagesConfig(Domain string, images []ImageInfo) error {
	imagesPath := fmt.Sprintf("%s/%s/images.json", PRODUCT_CONFIG_PATH, Domain)
	if !public.FileExists(PRODUCT_CONFIG_PATH + "/" + Domain) {
		os.MkdirAll(PRODUCT_CONFIG_PATH+"/"+Domain, os.ModePerm)
	}
	data, err := json.MarshalIndent(images, "", "  ")
	if err != nil {
		return fmt.Errorf("error marshalling images: %v", err)
	}
	err = os.WriteFile(imagesPath, data, 0644)
	if err != nil {
		return fmt.Errorf("error saving images file: %v", err)
	}
	return nil
}

func ReadImagesConfig(Domain string) ([]ImageInfo, error) {
	imagesPath := fmt.Sprintf("%s/%s/images.json", PRODUCT_CONFIG_PATH, Domain)
	if !public.FileExists(imagesPath) {
		os.WriteFile(imagesPath, []byte("[]"), 0644)
	}
	data, err := os.ReadFile(imagesPath)
	if err != nil {
		return []ImageInfo{}, fmt.Errorf("error reading images configuration file: %v", err)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the wrapped 'json: unsupported type: ...' message to identify the failing type
  2. Add json:"-" to non-serializable ImageInfo fields or implement MarshalJSON on them
  3. Replace unsupported fields with serializable equivalents (string IDs, plain structs)
  4. Guard against cycles by storing indices/IDs instead of self-referencing pointers

Example fix

// before
type ImageInfo struct {
	URL    string     `json:"url"`
	Loader func()     // causes: json: unsupported type: func()
}

// after
type ImageInfo struct {
	URL    string     `json:"url"`
	Loader func()     `json:"-"`
}
Defensive patterns

Strategy: validation

Validate before calling

// validate images slice is serializable before SaveImagesConfig
func imagesAreSerializable(images []ImageInfo) error {
	_, err := json.Marshal(images)
	return err
}
// usage:
if err := imagesAreSerializable(images); err != nil {
	log.Printf("images not serializable, fix ImageInfo fields: %v", err)
}

Try / catch

if err := SaveImagesConfig(domain, images); err != nil {
	if strings.Contains(err.Error(), "marshalling images") {
		log.Printf("ImageInfo contains unsupported field type: %v", err)
		// sanitize: strip non-serializable fields and retry
		return err
	}
	return err
}

Prevention

When it happens

Trigger: Calling SaveImagesConfig(domain, images) where an ImageInfo field (or nested struct) is a func, channel, or contains a reference cycle, causing json.MarshalIndent to return 'json: unsupported type'.

Common situations: A developer extended ImageInfo with a non-serializable field (handler, connection, sync.Mutex used carelessly in a cycle) without a json:"-" tag or custom MarshalJSON, then saved the images config.

Related errors


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