vxcontrol/pentagi · error
failed to get subtask msg logs: %w
Error message
failed to get subtask msg logs: %w
What it means
Returned by flowStatusTool.buildRunningInfo (backend/pkg/tools/flow_manager.go:343) when fetching the most recent agent message logs for the active subtask. appendSubtaskMsgLogs queries the database via t.db.GetSubtaskMsgLogs(ctx, subtaskID); any database error (connection failure, deadlock, cancelled context, missing table) is wrapped here. Unlike errors 755-758, the root cause is the database layer, not the LLM summarizer — the internal summarizer failure inside appendSubtaskMsgLogs produces a different message ('failed to summarize msg logs').
Source
Thrown at backend/pkg/tools/flow_manager.go:343
if verbose && st.Context != "" {
execContext, err := t.getDescriptionText(ctx, st.Context)
if err != nil {
return "", fmt.Errorf("failed to get subtask context: %w", err)
}
fmt.Fprintf(sb, "\nExecution context:\n%s\n", execContext)
}
if st.Status == database.SubtaskStatusWaiting {
fmt.Fprintf(sb, "\nNote: subtask is waiting for user input (ask state).\n")
fmt.Fprintf(sb, "Use %s to provide the answer and resume execution.\n", SubmitFlowInputToolName)
}
msgLimit := msgLogLimitNormal
if verbose {
msgLimit = msgLogLimitVerbose
}
msgLogs, err := t.appendSubtaskMsgLogs(ctx, st.ID, msgLimit)
if err != nil {
return "", fmt.Errorf("failed to get subtask msg logs: %w", err)
}
sb.WriteString(msgLogs)
runningInfo := sb.String()
if t.summarizer != nil && len(runningInfo) > runningInfoLimit {
runningInfo, err = t.summarizer(ctx, truncateText(runningInfo, summarizationLimit))
if err != nil {
return "", fmt.Errorf("failed to summarize running info: %w", err)
}
}
return runningInfo, nil
}
return "No running or waiting subtask found. Flow is idle (waiting for next input).", nil
}
func (t *flowStatusTool) buildPlannedList(ctx context.Context, taskID *int64, verbose bool) (string, error) {View on GitHub (pinned to ea665308ba)
Solutions
- Check the wrapped %w cause and backend logs for the concrete PostgreSQL error (connection refused, timeout, undefined table).
- Verify PostgreSQL is reachable and healthy (docker compose ps, pg_isready) and that backend/env DB credentials are correct.
- Run pending goose migrations (backend/migrations/sql) to ensure the msg log tables exist.
- Retry the flow_status call after the DB recovers — transient connection errors self-heal once pooling reconnects.
- Increase connection pool limits / statement timeouts if pool exhaustion under load is the cause.
Example fix
// before: no retry on transient DB failure
logs, err := t.db.GetSubtaskMsgLogs(ctx, nullID)
if err != nil {
return "", fmt.Errorf("failed to get subtask msg logs: %w", err)
}
// after: bounded retry for transient connection errors
var logs []database.SubtaskMsgLog
err = retry(ctx, 3, 500*time.Millisecond, func() error {
var e error
logs, e = t.db.GetSubtaskMsgLogs(ctx, nullID)
return e
})
if err != nil {
return "", fmt.Errorf("failed to get subtask msg logs: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// before invoking the tool, confirm the database accepts queries
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("database unavailable: %w", err)
}
// ensure migrations applied
down, _ := migrationsPending(ctx)
if down {
return fmt.Errorf("pending database migrations; run goose up")
} Type guard
func isTransientDBError(err error) bool {
if err == nil {
return false
}
err = errors.Unwrap(err)
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
return pgcode.IsConnectionException(pgErr.Code) || pgErr.Code == pgcode.SerializationFailure
}
return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.EOF)
} Try / catch
logs, err := appendSubtaskMsgLogs(ctx, st.ID, limit)
if err != nil {
if isTransientDBError(err) {
// retry after backoff — pooled connections usually recover
logs, err = retryWithBackoff(ctx, 3, 500*time.Millisecond, func() (string, error) {
return appendSubtaskMsgLogs(ctx, st.ID, limit)
})
}
if err != nil {
return "", fmt.Errorf("failed to get subtask msg logs: %w", err)
}
} Prevention
- Run goose migrations at startup so msg log tables always exist.
- Set sensible pool size and statement timeouts (pgxpool defaults) for agent concurrency.
- Add health checks (pg_isready) in Docker Compose with depends_on conditions.
- Alert on connection pool saturation and PostgreSQL failover events.
- Wrap DB queries with bounded retry for transient connection/serialization errors.
When it happens
Trigger: flow_status tool invoked while a subtask is running/waiting and the PostgreSQL query GetSubtaskMsgLogs fails: DB connection dropped/pooled out, PostgreSQL restart, statement timeout, ctx cancelled mid-query, or schema drift (missing msg_logs table if migrations did not run).
Common situations: Database outage or failover during a flow; connection pool exhaustion under heavy agent concurrency; goose migrations not applied so GetSubtaskMsgLogs references a missing table/column; network partition between the backend container and the postgres container in Docker Compose.
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
- failed to set flow %d status: %w
- failed to renew flow %d status: %w
- failed to get flow %d status: %w
- failed to get flow subtasks: %w
- failed to get subtasks: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/755c1bed327f1459.
Report an issue: GitHub.