router-for-me/CLIProxyAPI · error

plugin sync response is nil

Error message

plugin sync response is nil

What it means

PluginSyncResponse.Validate was invoked on a nil *PluginSyncResponse. Validate is a pointer method that dereferences fields, so it guards against nil receivers first. In practice this indicates a bug in the caller — the response object was never populated (e.g. a failed fetch returned nil and the error was ignored) before validation.

Source

Thrown at internal/pluginstore/home_sync.go:49

func (i *PluginSyncItem) Clear() {
	if i == nil {
		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)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Check the error returned by the sync-fetch call before calling Validate
  2. Ensure the response variable is assigned from a successful fetch in every code path
  3. Add a nil check on the response pointer at the call site for defensive clarity

Example fix

// before
resp, _ := fetchSync(ctx)
err := resp.Validate(time.Now()) // panic risk / nil error

// after
resp, err := fetchSync(ctx)
if err != nil {
    return err
}
if err := resp.Validate(time.Now()); err != nil {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

if resp == nil {
    return errors.New("sync response is nil; fetch failed or was skipped")
}

Type guard

func hasSyncResponse(r *pluginstore.PluginSyncResponse) bool { return r != nil }

Try / catch

if err := resp.Validate(now); err != nil && strings.Contains(err.Error(), "is nil") {
    // fix the fetch path that returned nil without propagating its error
}

Prevention

When it happens

Trigger: Calling var r *PluginSyncResponse; r.Validate(now) or passing the result of a sync fetch whose error was ignored, leaving the pointer nil.

Common situations: Ignoring the error from the sync-fetch call and proceeding to Validate; early-return paths that skip assignment; refactor moving validation before population.

Related errors


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