router-for-me/CLIProxyAPI · error

unsupported plugin sync schema_version

Error message

unsupported plugin sync schema_version %d

What it means

The plugin sync response's schema_version field does not equal PluginSyncSchemaVersion (currently 1). The sync protocol is versioned so that clients and the plugin index server agree on the payload shape; any other value is rejected before further parsing. This appears when the server is newer (or older) than the client understands.

Solutions

  1. Upgrade the client binary to the version matching the sync server's schema
  2. Delete the cached sync response (it will be re-fetched with a compatible schema once versions align)
  3. If you operate the sync server, pin it to schema_version 1 until all clients upgrade
  4. Verify the sync endpoint URL points at the official plugin index
Defensive patterns

Strategy: validation

Validate before calling

if resp.SchemaVersion != pluginstore.PluginSyncSchemaVersion {
    return fmt.Errorf("cache schema %d unsupported, refetch", resp.SchemaVersion)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "unsupported plugin sync schema_version") {
    // drop cache, refetch; if it persists, upgrade/downgrade client to match server
}

Prevention

When it happens

Trigger: PluginSyncResponse.Validate on a payload whose schema_version is 0 (missing field, old server) or >= 2 (newer server format), i.e. after a client/server version skew.

Common situations: Upgraded CLIProxyAPI talks to an old cached sync index; downgraded binary reads a cache written by a newer schema; sync endpoint URL pointed at an incompatible service.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/c6cef19be0267380. Report an issue: GitHub.

Appendix: source

Thrown at internal/pluginstore/home_sync.go:52

		return
	}
	ClearResolvedAuthConfigs(i.Auth)
	i.Auth = nil
	i.Manifest = Manifest{}
}

type PluginSyncResponse struct {
	SchemaVersion int              `json:"schema_version"`
	ExpiresAt     time.Time        `json:"expires_at"`
	Items         []PluginSyncItem `json:"items"`
}

func (r *PluginSyncResponse) Validate(now time.Time) error {
	if r == nil {
		return fmt.Errorf("plugin sync response is nil")
	}
	if r.SchemaVersion != PluginSyncSchemaVersion {
		return fmt.Errorf("unsupported plugin sync schema_version %d", r.SchemaVersion)
	}
	if r.ExpiresAt.IsZero() {
		return fmt.Errorf("plugin sync response missing expires_at")
	}
	if !now.Before(r.ExpiresAt) {
		return fmt.Errorf("plugin sync response expired")
	}
	seen := make(map[string]struct{}, len(r.Items))
	for index := range r.Items {
		item := &r.Items[index]
		if errManifest := item.Manifest.Validate(); errManifest != nil {
			return fmt.Errorf("plugin sync item %d: %w", index, errManifest)
		}
		if errURLs := validatePluginSyncManifestURLs(item.Manifest); errURLs != nil {
			return fmt.Errorf("plugin sync item %d: %w", index, errURLs)
		}
		id := strings.TrimSpace(item.Manifest.ID)
		if _, exists := seen[id]; exists {

View on GitHub (pinned to 78f0c4079e)