charmbracelet/crush · error

failed to marshal provider data: %w

Error message

failed to marshal provider data: %w

What it means

Thrown by cache.Store() when json.Marshal fails to serialize the provider data being cached. Rare in practice because the value is a well-typed struct, but can occur with values containing unsupported types (channels, funcs, cyclic references) when the generic cache[T] is used with a non-marshalable T.

Source

Thrown at internal/config/provider.go:292

		return v, "", fmt.Errorf("failed to read provider cache file: %w", err)
	}

	if err := json.Unmarshal(data, &v); err != nil {
		return v, "", fmt.Errorf("failed to unmarshal provider data from cache: %w", err)
	}

	return v, etag.Of(data), nil
}

func (c cache[T]) Store(v T) error {
	slog.Info("Saving provider data to disk", "path", c.path)
	if err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil {
		return fmt.Errorf("failed to create directory for provider cache: %w", err)
	}

	data, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("failed to marshal provider data: %w", err)
	}

	// Written through a temporary file and renamed into place. Several Crush
	// instances start independently and race to refresh this cache, and a
	// truncating write would let one of them read a half-written catalog and
	// silently fall back to the bundled copy.
	if err := atomicWriteFile(c.path, data, 0o644); err != nil {
		return fmt.Errorf("failed to write provider data to cache: %w", err)
	}
	return nil
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the value being stored for unsupported JSON types (chan, func, cycles).
  2. Fix the producer of the data to only include JSON-serializable fields.
  3. Add json:"-" tags to non-serializable fields in the type parameter T.
  4. Wrap Marshal yourself with a custom codec if you need special serialization.

Example fix

// before
type P struct { Conn *net.Conn }
// after
type P struct { Conn *net.Conn `json:"-"` }
Defensive patterns

Strategy: validation

Validate before calling

if err := json.Marshal(v); err != nil {
    // value not serializable; fix before Store
}

Type guard

func marshalable[T any](v T) bool {
    _, err := json.Marshal(v)
    return err == nil
}

Try / catch

if err := cache.Store(v); err != nil {
    if strings.Contains(err.Error(), "marshal") {
        log.Warn("Skipping cache: value not serializable")
    }
}

Prevention

When it happens

Trigger: cache.Store(v) is called with a T that json.Marshal cannot encode: unsupported field types, cycles, or invalid values (NaN) injected into the provider data by a caller.

Common situations: Custom builds/extensions feeding non-serializable data into the generic cache; middlewares mutating provider structs with extra fields of unsupported types.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/3f685d1d26d3de25. Report an issue: GitHub.