JuliusBrussee/caveman · error
postgres: organization scope is required
Error message
postgres: organization scope is required
What it means
postgresconfig.WithOrg wraps tenant work in a transaction and sets app.current_organization_id (transaction-local) so row-level security has a scope. It rejects an empty or whitespace orgID up front because an unscoped transaction would let a query degrade to unfiltered access — the opposite of what the RLS boundary exists for. The error is thrown before pool.Begin, so no database resources are touched.
Source
Thrown at shared/platform/postgresconfig/postgresconfig.go:767
)
SELECT coalesce(string_agg(violation, '; ' ORDER BY violation), '') FROM violations
`).Scan(&violations)
if err != nil {
return fmt.Errorf("postgres: inspect resolver schema: %w", err)
}
if violations != "" {
return fmt.Errorf("postgres: resolver isolation incomplete: %s", violations)
}
return nil
}
// WithOrg runs fn inside a transaction whose tenant GUC is transaction-local.
// Empty scopes are rejected: tenant work must never degrade to an unscoped
// query when RLS is the hard boundary.
func WithOrg(ctx context.Context, pool *pgxpool.Pool, orgID string, fn func(pgx.Tx) error) error {
orgID = strings.TrimSpace(orgID)
if orgID == "" {
return errors.New("postgres: organization scope is required")
}
tx, err := pool.Begin(ctx)
if err != nil {
return err
}
defer func() { _ = tx.Rollback(ctx) }()
if _, err := tx.Exec(ctx, `SELECT set_config('app.current_organization_id', $1, true)`, orgID); err != nil {
return fmt.Errorf("postgres: set organization scope: %w", err)
}
if err := fn(tx); err != nil {
return err
}
return tx.Commit(ctx)
}
View on GitHub (pinned to 766dce6b13)
Solutions
- Resolve the organization ID before the call and fail the request when it is missing (401/400) instead of attempting tenant work
- Fix the middleware order so the org context value is populated for every route that reaches WithOrg
- For non-tenant (platform-level) work, use an explicit non-WithOrg code path rather than passing an empty scope
Example fix
// before
orgID, _ := ctx.Value(orgKey).(string)
err := postgresconfig.WithOrg(ctx, pool, orgID, fn)
// after
orgID, ok := ctx.Value(orgKey).(string)
if !ok || strings.TrimSpace(orgID) == "" {
return errors.New("organization context missing")
}
err := postgresconfig.WithOrg(ctx, pool, orgID, fn) Defensive patterns
Strategy: validation
Validate before calling
orgID, ok := ctx.Value(orgContextKey).(string)
if !ok || strings.TrimSpace(orgID) == "" {
return errors.New("organization context required")
} Type guard
func hasOrgScope(ctx context.Context) bool {
orgID, ok := ctx.Value(orgContextKey).(string)
return ok && strings.TrimSpace(orgID) != ""
} Try / catch
if err := postgresconfig.WithOrg(ctx, pool, orgID, fn); err != nil {
if strings.Contains(err.Error(), "organization scope is required") {
// request reached tenant code without an org ID; check middleware order
}
return err
} Prevention
- Register org-extraction middleware before any route that performs tenant work
- Reject org-less requests at the handler boundary with 400/401 so this guard never fires in production
When it happens
Trigger: Calling WithOrg(ctx, pool, "") or WithOrg(ctx, pool, " ") — typically the org ID was extracted from a request context, JWT claim, or URL path that was absent (middleware did not set it, anonymous route, or upstream parsing produced an empty string).
Common situations: An endpoint registered before the org-extraction middleware; a background job invoked without the per-tenant parameter; a refactor switching from org slug to UUID leaving old callers passing ""; tests exercising the handler directly without seeding the org context value.
Related errors
- postgres: expected runtime role is required
- postgres: production DATABASE_URL must be a Postgres URL
- recordOutcome: values must be a non-empty plain object
- recordOutcome: evidence must be a plain object
- recordOutcome: evidence keys must be non-empty
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/6512cab43a38cb6d.
Report an issue: GitHub.