router-for-me/CLIProxyAPI · error
plugin sync response missing expires_at
Error message
plugin sync response missing expires_at
What it means
The plugin sync response passed validation of schema_version but its expires_at time.Time is the zero value, meaning the field was absent or unparseable in the JSON. Every sync payload must carry a meaningful expiry so stale indexes are never used; a zero value is treated as malformed data.
Source
Thrown at internal/pluginstore/home_sync.go:55
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 {
return fmt.Errorf("plugin sync response contains duplicate plugin %q", id)
}
seen[id] = struct{}{}View on GitHub (pinned to 78f0c4079e)
Solutions
- Inspect the raw sync JSON and confirm expires_at is present and RFC3339-formatted
- Fix the sync server/index generator to always emit expires_at
- Re-fetch the index from the official plugin store URL to get a well-formed payload
Defensive patterns
Strategy: validation
Validate before calling
if resp.ExpiresAt.IsZero() {
return errors.New("sync payload missing expires_at; treat as malformed")
} Try / catch
if err != nil && strings.Contains(err.Error(), "missing expires_at") {
// discard payload, refetch; report to index operator if persistent
} Prevention
- Index generators: always stamp expires_at (RFC3339)
- Run the sync payload through Validate before caching it
- Log raw payload shape when expiry validation fails to catch format drift
When it happens
Trigger: Validate on a sync response where the JSON lacked expires_at, it was null, or the timestamp format was not parseable by encoding/json (non-RFC3339).
Common situations: Hand-crafted or third-party sync indexes omitting expiry; a server bug dropping the field; a timestamp format change on the server.
Related errors
- weight must be an integer
- invalid auth json: %w
- unsupported plugin sync schema_version %d
- plugin sync item %d: %w
- plugin sync response contains duplicate plugin %q
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/a99a9f322b6dfd43.
Report an issue: GitHub.