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

  1. Check the wrapped %w cause and backend logs for the concrete PostgreSQL error (connection refused, timeout, undefined table).
  2. Verify PostgreSQL is reachable and healthy (docker compose ps, pg_isready) and that backend/env DB credentials are correct.
  3. Run pending goose migrations (backend/migrations/sql) to ensure the msg log tables exist.
  4. Retry the flow_status call after the DB recovers — transient connection errors self-heal once pooling reconnects.
  5. 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

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


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