vxcontrol/pentagi · error

failed to load user prompts: %w

Error message

failed to load user prompts: %w

What it means

newUserPrompter loads a user's custom prompt overrides via db.GetUserPrompts(ctx, userID) and overlays them on the embedded default templates. If that database query fails, session/worker creation fails explicitly with "failed to load user prompts: %w" — by design it does NOT silently fall back to defaults, because running agents with wrong (default-only) prompts would be worse than failing loudly.

Source

Thrown at backend/pkg/controller/prompter.go:19

package controller

import (
	"context"
	"fmt"

	"pentagi/pkg/database"
	"pentagi/pkg/templates"
)

// newUserPrompter loads the user's custom prompts from the database and
// overlays them onto the compiled default templates. Prompt types that
// the user has not customized continue to use the defaults. A database
// error is returned to the caller so that session creation fails
// explicitly instead of silently falling back to defaults.
func newUserPrompter(ctx context.Context, db database.Querier, userID int64) (templates.Prompter, error) {
	userPrompts, err := db.GetUserPrompts(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("failed to load user prompts: %w", err)
	}

	defaults, err := templates.LoadDefaultPromptsMap()
	if err != nil {
		return nil, fmt.Errorf("failed to load default templates: %w", err)
	}

	return buildUserPrompter(defaults, userPrompts), nil
}

// buildUserPrompter is the pure merge step extracted from newUserPrompter so
// it can be unit-tested without a database fake or filesystem access. It
// mutates the supplied defaults map by overlaying each non-empty user
// override on top, then returns a Prompter backed by that map. Callers must
// pass a fresh map (e.g., from templates.LoadDefaultPromptsMap) so the
// embedded defaults are not modified.
func buildUserPrompter(defaults templates.PromptsMap, userPrompts []database.Prompt) templates.Prompter {
	for _, p := range userPrompts {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Inspect the wrapped cause to identify the SQL/driver error.
  2. Verify PostgreSQL connectivity and DATABASE_URL configuration.
  3. Retry worker creation once the DB is healthy — nothing is cached from the failed attempt.
  4. Check DB pool settings if errors appear only under load.

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

// check DB reachability before creating workers
if err := db.Ping(ctx); err != nil {
    return fmt.Errorf("database unavailable: %w", err)
}

Type guard

null

Try / catch

prompter, err := newUserPrompter(ctx, db, userID)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || isTransientDBError(err) {
        // brief backoff then retry session creation
        time.Sleep(time.Second)
        prompter, err = newUserPrompter(ctx, db, userID)
    }
    return err
}

Prevention

When it happens

Trigger: Any call path that creates or loads a flow/assistant worker (NewFlowWorker, LoadFlowWorker, NewAssistantWorker, LoadAssistantWorker) when GetUserPrompts fails: DB connection error, context cancellation, missing user row constraints, or connection pool exhaustion.

Common situations: PostgreSQL down or restarting when a user starts a flow; expired/cancelled request context; DB pool exhausted under heavy concurrent flow creation; misconfigured DATABASE_URL after deployment.

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/1f827ec9f79cf9ed. Report an issue: GitHub.