derailed/k9s · info
refresh in progress, dropping
Error message
refresh in progress, dropping
What it means
RevValues.refresh implements a single-flight guard with atomic.CompareAndSwapInt32(&v.inUpdate, 0, 1): if a refresh/reconcile is already running, the second caller logs 'Dropping update...' and returns this error. It is a deliberate, benign drop of a redundant tick — the watcher loop that triggers refresh (rev_values.go:71/124/129/147) simply skips that cycle and the next tick re-syncs. The same pattern guards model1 Table/Yaml/Tree/Describe values.
Source
Thrown at internal/model/rev_values.go:164
case <-time.After(delay):
if err := v.refresh(ctx); err != nil {
v.fireResourceFailed(err)
if delay = backOff.NextBackOff(); delay == backoff.Stop {
slog.Error("Giving up retrieving chart values", slogs.Error, err)
return
}
} else {
backOff.Reset()
delay = defaultReaderRefreshRate
}
}
}
}
func (v *RevValues) refresh(context.Context) error {
if !atomic.CompareAndSwapInt32(&v.inUpdate, 0, 1) {
slog.Debug("Dropping update...")
return errors.New("refresh in progress, dropping")
}
defer atomic.StoreInt32(&v.inUpdate, 0)
v.reconcile()
return nil
}
func (v *RevValues) reconcile() {
v.fireResourceChanged(v.lines, v.filter(v.query, v.lines))
}
// AddListener adds a new model listener.
func (v *RevValues) AddListener(l ResourceViewerListener) {
v.listeners = append(v.listeners, l)
}
// RemoveListener delete a listener from the list.View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Treat it as a no-op signal: log at debug level and skip (the shipped callers already log 'Dropping update...').
- If data latency matters, raise the reader refresh rate so refreshes rarely overlap, or coalesce events before calling refresh.
- Do not retry immediately in the same goroutine — the in-flight refresh will publish fresher data anyway.
Example fix
// before
if err := v.refresh(ctx); err != nil { slog.Error("refresh failed", "err", err) }
// after
if err := v.refresh(ctx); err != nil {
if !errors.Is(err, errRefreshInFlight) { slog.Error("refresh failed", "err", err) }
} Defensive patterns
Strategy: retry
Try / catch
if err := v.refresh(ctx); err != nil {
if strings.Contains(err.Error(), "refresh in progress, dropping") {
return // benign single-flight drop; next tick reconciles
}
return err
} Prevention
- Treat this error as a skip signal, never as a failure to report.
- Tune refresh rate if drops are frequent enough to cause stale views.
- Reuse the same CompareAndSwap guard pattern in custom value models.
When it happens
Trigger: A watcher event or refresh timer firing while a previous refresh is still executing — large CRD/value payloads, slow terminals, or bursts of cluster events make refreshes overlap; every overlapping call gets this error.
Common situations: Viewing helm values / large ConfigMaps on slow connections where reconciliation outlasts the refresh interval; high churn clusters; it surfaces in logs as noise rather than as a user-visible failure.
Related errors
- no benchmark dir found in context
- no path specified in context
- expecting context GVR
- expecting context Path
- expecting context Wait
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/82e96eedb1a16c32.
Report an issue: GitHub.