jackc/pgx · error

watch already in progress

Error message

watch already in progress

What it means

ContextWatcher.Watch panics when a watch is already active (stop channel non-nil), i.e. Watch was called twice without an intervening Unwatch — the watcher tracks a single context at a time.

Solutions

  1. Call Unwatch before starting a new Watch
  2. Watch a single all-encompassing context instead of swapping contexts mid-operation
  3. Pair Watch/Unwatch calls symmetrically at call sites (e.g. defer Unwatch immediately after Watch)
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pgconn/ctxwatch/context_watcher.go:37 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /api/errors/df77362af400d7ae. Report an issue: GitHub.

Appendix: source

Thrown at pgconn/ctxwatch/context_watcher.go:37

// NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled.
// OnUnwatchAfterCancel will be called when Unwatch is called and the watched context had already been canceled and
// onCancel called.
func NewContextWatcher(handler Handler) *ContextWatcher {
	cw := &ContextWatcher{
		handler: handler,
	}

	return cw
}

// Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called.
func (cw *ContextWatcher) Watch(ctx context.Context) {
	cw.lock.Lock()
	defer cw.lock.Unlock()

	if cw.stop != nil {
		panic("watch already in progress")
	}

	if ctx.Done() != nil {
		cw.done = make(chan struct{})
		cw.stop = context.AfterFunc(ctx, func() {
			cw.handler.HandleCancel(ctx)
			close(cw.done)
		})
	}
}

// Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was
// called then onUnwatchAfterCancel will also be called.
func (cw *ContextWatcher) Unwatch() {
	cw.lock.Lock()
	defer cw.lock.Unlock()

	if cw.stop != nil {

View on GitHub (pinned to ec1a0befd2)