JuliusBrussee/caveman · critical · Error

cave_eve_runtime_identity_missing

cave_eve_runtime_identity_missing

Error message

cave_eve_runtime_identity_missing

What it means

Returned by ValidateTenantSchema when tenantSchemaViolations finds at least one breach of the structural isolation invariants: a tenant table without FORCE RLS, a missing or wrong-organization_id RLS policy expression (must equal current_setting('app.current_organization_id', ...) with optional casts), a nullable organization_id where it must not be, or a tenant-to-tenant foreign key that does not share the organization scope (plus project scope when both sides have it). Violations are joined with '; ' into one message. This is a security invariant failure: the schema as deployed permits cross-tenant access.

Source

Thrown at packages/agent/src/adapters.ts:480

    totalTokens: strictInteger(value.totalTokens, error),
  };
  if (usage.totalTokens !== usage.inputTokens + usage.outputTokens +
      usage.cacheReadTokens + usage.cacheWriteTokens ||
      usage.reasoningTokens > usage.outputTokens) {
    throw new Error(error);
  }
  return usage;
}

function eveRuntimeIdentity(events: unknown[]): {
  provider: string;
  model: string;
  upstreamVersion: string;
} {
  const starts = events.filter((event) =>
    isRecord(event) && event.type === "session.started" && isRecord(event.data) &&
    isRecord(event.data.runtime));
  if (starts.length !== 1) throw new Error("cave_eve_runtime_identity_missing");
  const runtime = (starts[0] as { data: { runtime: Record<string, unknown> } }).data.runtime;
  if (typeof runtime.modelId !== "string" || runtime.modelId.length === 0 ||
      runtime.modelId.startsWith("dynamic:")) {
    throw new Error("cave_eve_runtime_identity_missing");
  }
  const separator = runtime.modelId.indexOf("/");
  if (separator < 1 || separator === runtime.modelId.length - 1 ||
      typeof runtime.eveVersion !== "string" || runtime.eveVersion.length === 0) {
    throw new Error("cave_eve_runtime_identity_missing");
  }
  return {
    provider: runtime.modelId.slice(0, separator),
    model: runtime.modelId.slice(separator + 1),
    upstreamVersion: runtime.eveVersion,
  };
}

function expectedProviderModel(plan: CavePlan): { provider: string; model: string } {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Run ValidateTenantSchema (or the underlying catalog queries) in a dev database and match each violation string to the specific table/policy it names
  2. For every flagged table: ENABLE and FORCE row level security, create/repair a policy whose USING/WITH CHECK expression equals organization_id = current_setting('app.current_organization_id', true) (allowed casts per the regexp), and make organization_id NOT NULL if required
  3. Repair flagged foreign keys so they reference (organization_id, id) of the peer tenant table (and include project_id when both tables carry it)
  4. Add the same checks to CI/migration tests so a non-compliant migration fails before deploy

Example fix

-- before: tenant table without forced RLS
CREATE TABLE invoices (
  id bigint PRIMARY KEY,
  organization_id bigint NOT NULL REFERENCES organizations(id),
  ...
);  -- validation fails: rls not enabled/forced, policy missing

-- after
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
  USING (organization_id = current_setting('app.current_organization_id', true))
  WITH CHECK (organization_id = current_setting('app.current_organization_id', true));
Defensive patterns

Strategy: validation

Validate before calling

// Standalone check developers can run in a dev DB or migration test:
func assertTenantRLS(ctx context.Context, conn pgx Querier, table string) error {
    var rls, force, hasPolicy bool
    var notNullOK bool
    err := conn.QueryRow(ctx, `
        SELECT c.relrowsecurity, c.relforcerowsecurity,
               EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid = c.oid),
               EXISTS (SELECT 1 FROM pg_attribute a
                        WHERE a.attrelid = c.oid AND a.attname = 'organization_id' AND a.attnotnull)
          FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
         WHERE n.nspname = 'public' AND c.relname = $1`, table).Scan(&rls, &force, &hasPolicy, &notNullOK)
    if err != nil { return err }
    if !rls || !force || !hasPolicy || !notNullOK {
        return fmt.Errorf("table %s violates tenant isolation (rls=%t force=%t policy=%t org_not_null=%t)", table, rls, force, hasPolicy, notNullOK)
    }
    return nil
}

Type guard

func isIsolationViolationErr(err error) bool {
    return err != nil && strings.HasPrefix(err.Error(), "postgres: tenant schema isolation incomplete")
}

Try / catch

if err := postgresconfig.ValidateTenantSchema(ctx, pool); err != nil {
    if isIsolationViolationErr(err) {
        // SECURITY: do not catch-and-continue serving traffic.
        // Parse the '; '-joined violations, fix each table's RLS/policy/FK in migrations, redeploy.
        log.Fatalf("tenant isolation violated: %v", err)
    }
    return err // other errors: infra, see indices 15-17,19
}

Prevention

When it happens

Trigger: Adding a tenant table (one with organization_id) in a migration without ALTER TABLE ... ENABLE ROW LEVEL SECURITY / FORCE ROW LEVEL SECURITY and a matching policy; changing a policy expression so it no longer references app.current_organization_id; adding an FK from one tenant table to another keyed only on id; making organization_id nullable on a table the checker requires to be scoped.

Common situations: A new feature table slipped into a migration without the RLS boilerplate; a DBA 'optimizing' or disabling RLS on a slow table; refactoring the tenant-context GUC name without updating every policy; backfills that drop NOT NULL on organization_id temporarily and shipping before restoring.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/05c95d0efdb95bf8. Report an issue: GitHub.