vxcontrol/pentagi · critical

failed to create flow in DB: %w

Error message

failed to create flow in DB: %w

What it means

NewFlowWorker creates the flow row via db.CreateFlow before anything else; if the insert fails the worker cannot exist, so it logs via obs.LogErrorOrCancel and wraps the DB error with 'failed to create flow in DB'. The wrapped error carries the underlying database/SQLC cause (connection failure, constraint violation, context cancellation).

Source

Thrown at backend/pkg/controller/flow.go:145

	fwc newFlowWorkerCtx,
) (_ FlowWorker, err error) {
	ctx, span := obs.Observer.NewSpan(ctx, obs.SpanKindInternal, "controller.NewFlowWorker")
	defer span.End()

	flow, err := fwc.db.CreateFlow(ctx, database.CreateFlowParams{
		Title:              "untitled",
		Status:             database.FlowStatusCreated,
		Model:              "unknown",
		ModelProviderName:  fwc.prvname.String(),
		ModelProviderType:  database.ProviderType(fwc.prvtype),
		Language:           "English",
		ToolCallIDTemplate: cast.ToolCallIDTemplate,
		Functions:          []byte("{}"),
		UserID:             fwc.userID,
	})
	if err != nil {
		obs.LogErrorOrCancel(logrus.WithContext(ctx), err, "failed to create flow in DB")
		return nil, fmt.Errorf("failed to create flow in DB: %w", err)
	}

	logger := logrus.WithContext(ctx).WithFields(logrus.Fields{
		"flow_id":       flow.ID,
		"user_id":       fwc.userID,
		"provider_name": fwc.prvname.String(),
		"provider_type": fwc.prvtype.String(),
	})
	logger.Info("flow created in DB")

	// Held separately because `flow` is reassigned below by UpdateFlow, which — like every sqlc query —
	// returns a zero-valued row alongside an error, and a cleanup aimed at id 0 deletes nothing.
	flowID := flow.ID

	// DeleteFlow is a soft delete and the listings filter on deleted_at, so this is what keeps a flow
	// the caller was told it never got out of the UI. Disarmed once the worker goroutine owns the flow —
	// from there a failure is the worker's to unwind, not ours.
	cleanupFlow := true

View on GitHub (pinned to ea665308ba)

Solutions

  1. Check PostgreSQL is reachable and credentials (POSTGRES_* env vars) are correct.
  2. Inspect the wrapped %w cause in logs for the specific SQL error (connection refused vs constraint vs cancel).
  3. Run goose migrations to ensure the flows table and enums exist.
  4. If the cause is context cancellation, keep the HTTP request alive long enough or use context.WithoutCancel for startup inserts.
  5. Check connection pool limits if the error appears only under high concurrency.

Example fix

// before
ctx := r.Context() // cancelled when client disconnects, aborting insert
flow, err := controller.NewFlowWorker(ctx, fwc)
// after
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
defer cancel()
flow, err := controller.NewFlowWorker(ctx, fwc)
if err != nil {
    log.WithError(errors.Unwrap(err)).Error("flow creation failed") // inspect root cause
}
Defensive patterns

Strategy: try-catch

Validate before calling

if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("database unreachable: %w", err)
}
if userID <= 0 { return errors.New("valid userID required") }

Try / catch

fw, err := controller.NewFlowWorker(ctx, fwc)
if err != nil {
    var root = errors.Unwrap(err)
    if errors.Is(root, context.Canceled) { /* client gone: no retry */ }
    else { /* transient DB issue: retry with backoff */ }
    log.WithError(root).Error("flow creation failed")
}

Prevention

When it happens

Trigger: db.CreateFlow returns an error: database unreachable/down, migration missing tables, context cancelled by the client while inserting, invalid UserID foreign key, or connection pool exhaustion.

Common situations: Postgres container stopped or misconfigured DB host/port in .env; client disconnected mid-request cancelling ctx; FK violation because the user was deleted concurrently; DB pool exhausted under load.

Related errors


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