vxcontrol/pentagi · error

failed to get containers: %w

Error message

failed to get containers: %w

What it means

GetContainers lists all containers for the worker's flow via db.GetFlowContainers and wraps any database failure. It is also called internally by PutMsg when refreshing the container cache, in which case this error propagates out of PutMsg unchanged (it is returned raw, not re-wrapped).

Source

Thrown at backend/pkg/controller/termlog.go:96

	tlw.pub.TerminalLogAdded(ctx, termLog)

	return termLog.ID, nil
}

func (tlw *flowTermLogWorker) GetMsg(ctx context.Context, msgID int64) (database.Termlog, error) {
	msg, err := tlw.db.GetTermLog(ctx, msgID)
	if err != nil {
		return database.Termlog{}, fmt.Errorf("failed to get termlog: %w", err)
	}

	return msg, nil
}

func (tlw *flowTermLogWorker) GetContainers(ctx context.Context) ([]database.Container, error) {
	containers, err := tlw.db.GetFlowContainers(ctx, tlw.flowID)
	if err != nil {
		return nil, fmt.Errorf("failed to get containers: %w", err)
	}

	return containers, nil
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check the wrapped error for connection vs timeout vs constraint causes
  2. Verify DB health (docker compose ps, pg_isready) and connection pool saturation
  3. Give container refreshes a background context with a sane timeout instead of a request ctx
  4. Add retry with backoff for transient errors before failing the terminal write
  5. Check indexes/latency on containers.flow_id if the query is slow

Example fix

// before
containers, err := termWorker.GetContainers(ctx) // request ctx may be canceled
if err != nil { return err }
// after
containers, err := termWorker.GetContainers(context.WithoutCancel(ctx))
if err != nil {
    if isTransient(err) { time.Sleep(backoff); containers, err = termWorker.GetContainers(ctx) }
    if err != nil { return err }
}
Defensive patterns

Strategy: retry

Validate before calling

func dbReachable(ctx context.Context, q database.Querier) error {
    c, err := q.Conn(ctx)
    if err != nil { return err }
    return c.Ping(ctx)
}

Type guard

func isTransientDBErr(err error) bool {
    return errors.Is(err, driver.ErrBadConn) ||
        errors.Is(err, context.DeadlineExceeded) ||
        errors.Is(err, pgconn.ErrTimeout)
}

Try / catch

containers, err := w.GetContainers(ctx)
if isTransientDBErr(err) {
    time.Sleep(250 * time.Millisecond)
    containers, err = w.GetContainers(ctx)
}
if err != nil {
    return nil, fmt.Errorf("list containers: %w", err)
}

Prevention

When it happens

Trigger: DB connection loss, canceled context, or query timeout while listing flow containers; also fires indirectly whenever PutMsg sees an unknown containerID and the refresh query fails.

Common situations: Postgres restarts under heavy terminal/agent load; slow queries on the containers table blocking the flow worker's mutex (PutMsg holds tlw.mx during refresh); context deadlines from request scoping.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/26e065c117a64234. Report an issue: GitHub.