{"record":{"id":"26e065c117a64234","repo":"vxcontrol/pentagi","slug":"failed-to-get-containers-w","errorCode":null,"errorMessage":"failed to get containers: %w","messagePattern":"failed to get containers: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/termlog.go","lineNumber":96,"sourceCode":"\n\ttlw.pub.TerminalLogAdded(ctx, termLog)\n\n\treturn termLog.ID, nil\n}\n\nfunc (tlw *flowTermLogWorker) GetMsg(ctx context.Context, msgID int64) (database.Termlog, error) {\n\tmsg, err := tlw.db.GetTermLog(ctx, msgID)\n\tif err != nil {\n\t\treturn database.Termlog{}, fmt.Errorf(\"failed to get termlog: %w\", err)\n\t}\n\n\treturn msg, nil\n}\n\nfunc (tlw *flowTermLogWorker) GetContainers(ctx context.Context) ([]database.Container, error) {\n\tcontainers, err := tlw.db.GetFlowContainers(ctx, tlw.flowID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get containers: %w\", err)\n\t}\n\n\treturn containers, nil\n}\n","sourceCodeStart":78,"sourceCodeEnd":101,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/termlog.go#L78-L101","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the wrapped error for connection vs timeout vs constraint causes","Verify DB health (docker compose ps, pg_isready) and connection pool saturation","Give container refreshes a background context with a sane timeout instead of a request ctx","Add retry with backoff for transient errors before failing the terminal write","Check indexes/latency on containers.flow_id if the query is slow"],"exampleFix":"// before\ncontainers, err := termWorker.GetContainers(ctx) // request ctx may be canceled\nif err != nil { return err }\n// after\ncontainers, err := termWorker.GetContainers(context.WithoutCancel(ctx))\nif err != nil {\n    if isTransient(err) { time.Sleep(backoff); containers, err = termWorker.GetContainers(ctx) }\n    if err != nil { return err }\n}","handlingStrategy":"retry","validationCode":"func dbReachable(ctx context.Context, q database.Querier) error {\n    c, err := q.Conn(ctx)\n    if err != nil { return err }\n    return c.Ping(ctx)\n}","typeGuard":"func isTransientDBErr(err error) bool {\n    return errors.Is(err, driver.ErrBadConn) ||\n        errors.Is(err, context.DeadlineExceeded) ||\n        errors.Is(err, pgconn.ErrTimeout)\n}","tryCatchPattern":"containers, err := w.GetContainers(ctx)\nif isTransientDBErr(err) {\n    time.Sleep(250 * time.Millisecond)\n    containers, err = w.GetContainers(ctx)\n}\nif err != nil {\n    return nil, fmt.Errorf(\"list containers: %w\", err)\n}","preventionTips":["Verify Postgres health (pg_isready, compose ps) when this fires repeatedly","Use a background context with timeout for internal container-cache refreshes","Watch connection-pool saturation under heavy agent/terminal load","Check indexes on containers.flow_id if listing is slow","Retry with backoff; never swallow the wrapped cause when surfacing to callers"],"tags":["database","postgresql","query"],"backgroundTag":"database-query-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}