googleapis/mcp-toolbox · warning

unable to close source %q: %w

Error message

unable to close source %q: %w

What it means

Returned by ConnectOnce.Close when releasing an already-initialized source connection fails. Close only attempts release if a connection is ready; if c.release(ctx, value) errors, the source's name and the underlying teardown error are wrapped. This indicates the resource cleanup (e.g., closing a DB pool) itself failed, not the shutdown flow.

Source

Thrown at internal/sources/connect.go:148

// Close does not wait for an attempt that is already in flight. That attempt
// releases its own result rather than caching it into a closed holder, so
// shutdown never blocks on a connect that may be hung for the full timeout.
func (c *ConnectOnce[T]) Close(ctx context.Context) error {
	c.mu.Lock()
	if c.closed {
		c.mu.Unlock()
		return nil
	}
	var zero T
	value, ready := c.value, c.ready
	c.value, c.ready, c.closed = zero, false, true
	c.mu.Unlock()

	if !ready {
		return nil
	}
	if err := c.release(ctx, value); err != nil {
		return fmt.Errorf("unable to close source %q: %w", c.name, err)
	}
	return nil
}

// Do returns the connection, making it on the first call. Concurrent callers
// share one attempt, and a failed attempt is not remembered.
//
// Because a failure is retried by the next caller rather than cached, connect
// must release whatever it had already built before it returns an error. A
// pool that fails its ping and is returned unclosed leaks once per tool call,
// not once per process.
func (c *ConnectOnce[T]) Do(ctx context.Context, connect func(context.Context) (T, error)) (T, error) {
	var zero T
	if value, ok := c.Get(); ok {
		return value, nil
	}
	if c.isClosed() {
		return zero, fmt.Errorf("unable to initialize source %q: source is closed", c.name)

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Inspect the wrapped cause (%w) to identify which underlying close operation failed
  2. Check network connectivity to the database if release involves waiting for connections to drain
  3. If this appears only at shutdown, it is usually benign — the process is exiting anyway; log and continue
  4. Update drivers/SDK versions if the wrapped error points at a known driver teardown bug
Defensive patterns

Strategy: try-catch

Try / catch

// Wrap source Close and log rather than crash at shutdown
if err := source.Close(ctx); err != nil {
	var closedErr *fmt.wrapError // inspect wrapped cause
	log.Printf("warning: source cleanup failed during shutdown: %v", err)
	// non-fatal at shutdown unless cleanup is contractually required
}

Prevention

When it happens

Trigger: Server shutdown calls Close on a source whose connection was successfully created, but c.release returns an error while closing the underlying resource (e.g., pool.Close() or driver Close failing, or the release function's context being cancelled/expired).

Common situations: Database already severed mid-shutdown so pool close hangs or errors, driver-level failures during teardown, or a context deadline exceeded while draining connections at process exit.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/a81a4f4daf4bceb9. Report an issue: GitHub.