JuliusBrussee/caveman · critical · Error
cave_harness_incomplete_evidence
cave_harness_incomplete_evidence
Error message
cave_harness_incomplete_evidence
What it means
Returned by validateRuntimeIdentity when the connected identity's current_user differs from session_user. The package requires the authenticated login identity and the effective identity to be identical, because Postgres allows session_user to run SET ROLE NONE and reclaim its original (possibly privileged) identity, which would silently escape the tenant-isolation role. Any connection that logged in as one role and SET ROLE'd into another is rejected.
Source
Thrown at packages/agent/src/adapters.ts:354
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheWriteTokens: number;
reasoningTokens: number;
totalTokens: number;
}
function harnessExecution(input: {
request: Readonly<HarnessRequest>;
text: string;
provider: string;
model: string;
usage: NormalizedUsage;
latencyMs: number;
}): HarnessExecution {
const expected = expectedProviderModel(input.request.plan);
if (input.provider !== expected.provider || input.model !== expected.model) {
throw new Error("cave_harness_incomplete_evidence");
}
const priced = validateProviderUsage({
provider: input.provider,
model: input.model,
...input.usage,
}, { requirePriced: true });
return {
terminal: true,
text: input.text,
provider: input.provider,
model: input.model,
...input.usage,
costUsd: priced.catalogCostUsd,
usageBasis: "provider_reported",
priceBasis: "public_catalog",
evaluatedTransformIDs: [...input.request.evaluatedTransformIDs],
appliedTransformIDs: [...input.request.appliedTransformIDs],
recoveryResolved: input.request.recoveryResolved,View on GitHub (pinned to 27d5a3981a)
Solutions
- Create a dedicated LOGIN role for the runtime pool (e.g. app_runtime) and put its credentials in DATABASE_URL so session_user == current_user from the start
- Remove any SET ROLE from connection startup SQL, pool 'after_connect' hooks, or PgBouncer startup parameters
- If PgBouncer is in front, use transaction pooling with clean state or bypass it for the runtime pool connections
- Re-run the identity query (SELECT current_user, session_user) with the same URL via psql to confirm both match
Example fix
# before DATABASE_URL=postgres://app_owner:...@db:5432/app?sslmode=verify-full # app_owner does: SET ROLE app_runtime; -> current_user != session_user -> rejected # after CREATE ROLE app_runtime LOGIN PASSWORD '...' NOLOGIN FALSE; GRANT app_runtime TO app_owner; -- if needed for grants DATABASE_URL=postgres://app_runtime:...@db:5432/app?sslmode=verify-full
Defensive patterns
Strategy: validation
Validate before calling
// Assert identity alignment before constructing the runtime pool:
func preflightIdentity(ctx context.Context, dbURL string) error {
conn, err := pgx.Connect(ctx, dbURL)
if err != nil { return err }
defer conn.Close(ctx)
var cur, sess string
if err := conn.QueryRow(ctx, "SELECT current_user, session_user").Scan(&cur, &sess); err != nil {
return err
}
if cur != sess {
return fmt.Errorf("login as the runtime role directly; %q logged in but effective role is %q (SET ROLE is not permitted)", sess, cur)
}
return nil
} Type guard
func isIdentityMismatchErr(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "postgres: runtime current_user")
} Try / catch
if err := preflightIdentity(ctx, dbURL); err != nil {
return err // fail with an operator-actionable message before pool construction
}
pool, err := postgresconfig.NewPool(ctx, dbURL) Prevention
- Provision a dedicated LOGIN role for the app and use only its credentials in DATABASE_URL
- Never rely on SET ROLE to drop privileges; connect as the least-privileged identity from the start
- If a pooler like PgBouncer sits in front, use transaction pooling and verify no session-level SET ROLE leaks
- Add a deployment smoke test that asserts current_user == session_user with the production URL (dummy query)
When it happens
Trigger: Connecting with a superuser/owner login and issuing SET ROLE app_runtime before handing the connection to the pool constructor; a proxy or pooler (PgBouncer) that reuses sessions with an active SET ROLE; DATABASE_URL credentials of an admin user with a startup parameter that changes the role.
Common situations: Dev setups where the operator connects as postgres and 'drops down' to the runtime role instead of creating a dedicated login; connection strings with options='-c role=app_runtime'; PgBouncer session pooling leaking role state between tenants; migration tools that connect as owner and then run the app in the same process.
Related errors
- cave_mastra_terminal_failure
- cave_vercel_usage_missing
- cave_eve_runtime_identity_missing
- cave_mastra_usage_missing
- listen address %q must be loopback host:port
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/3b2a617ba4f0ec7d.
Report an issue: GitHub.