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

  1. Treat it as a no-op signal: log at debug level and skip (the shipped callers already log 'Dropping update...').
  2. If data latency matters, raise the reader refresh rate so refreshes rarely overlap, or coalesce events before calling refresh.
  3. 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

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


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/82e96eedb1a16c32. Report an issue: GitHub.