amruthpillai/reactive-resume · error · ORPCError
PRECONDITION_FAILED
PRECONDITION_FAILED
Error message
AI agent workspace is unavailable because REDIS_URL or ENCRYPTION_SECRET is not configured.
What it means
A PRECONDITION_FAILED ORPCError raised by the mapAgentEnvironmentError middleware whenever the underlying service call throws an Error whose message is exactly 'AGENT_ENVIRONMENT_UNAVAILABLE'. That inner error comes from assertAgentEnvironment (packages/api/src/features/ai/credentials.ts), which requires both env.ENCRYPTION_SECRET (for AES-256-GCM credential encryption) and env.REDIS_URL (for resumable agent streaming) to be set. The message names both because either one missing disables the whole agent workspace.
Source
Thrown at packages/api/src/features/agent/routing.ts:10
import type { AnyMiddleware } from "@orpc/server";
import type { UIMessage } from "ai";
import { ORPCError } from "@orpc/client";
function isAgentEnvironmentUnavailable(error: unknown) {
return error instanceof Error && error.message === "AGENT_ENVIRONMENT_UNAVAILABLE";
}
function throwUnavailable(): never {
throw new ORPCError("PRECONDITION_FAILED", {
message: "AI agent workspace is unavailable because REDIS_URL or ENCRYPTION_SECRET is not configured.",
});
}
export function isUiMessage(value: unknown): value is UIMessage {
if (!value || typeof value !== "object") return false;
const message = value as Partial<UIMessage>;
return (
typeof message.id === "string" &&
(message.role === "system" || message.role === "user" || message.role === "assistant") &&
Array.isArray(message.parts)
);
}
// ponytail: single middleware replaces 12 near-identical try/catch blocks across agent route handlers
export const mapAgentEnvironmentError: AnyMiddleware = async ({ next }) => {
try {View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Add ENCRYPTION_SECRET (any non-empty secret, ideally 32+ random bytes) and REDIS_URL (e.g. redis://localhost:6379) to .env / .env.local and restart the server.
- If using Turborepo, confirm both names appear in turbo.json globalEnv or the relevant task env, otherwise they will be undefined inside the spawned process even when set in the OS.
- Start the Redis service (docker compose up -d redis or equivalent) and confirm connectivity before retrying.
- Guard the UI: hide/disable agent features until GET /api/rpc/agent.health (or equivalent capability flag) reports available, so users never reach this precondition.
Example fix
# before APP_URL=http://localhost:3000 DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres AUTH_SECRET=dev-secret # after (add the two agent env vars) APP_URL=http://localhost:3000 DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres AUTH_SECRET=dev-secret ENCRYPTION_SECRET=__generate_32_random_bytes__ REDIS_URL=redis://localhost:6379
Defensive patterns
Strategy: validation
Validate before calling
// Call a capability/health probe before exposing agent UI. const capable = await orpc.agent.capabilities(); // or whatever the capability route is if (!capable.agentEnvironment) hideAgentFeatures();
Type guard
function isAgentEnvConfigured(env: { REDIS_URL?: string; ENCRYPTION_SECRET?: string }): boolean {
return !!env.REDIS_URL?.trim() && !!env.ENCRYPTION_SECRET?.trim();
} Prevention
- Add REDIS_URL and ENCRYPTION_SECRET to the deployment env template (and to turbo.json globalEnv).
- Run a startup check that fails fast if the agent env is required but not configured.
- Keep the agent capability flag in the session so the UI can hide features before any call is attempted.
- Generate ENCRYPTION_SECRET once and treat it as a long-term secret; rotating it invalidates all stored provider keys.
When it happens
Trigger: Calling any agent route (threads.create, threads.getOrCreateForResume, messages.send, attachments.*) in a server process where REDIS_URL is unset or whitespace-only OR ENCRYPTION_SECRET is unset/whitespace; running the dev server without these in .env; deploying with Turborepo strict env mode without listing the vars in turbo.json globalEnv (so they get filtered out of child processes).
Common situations: Fresh checkout that only set APP_URL/DATABASE_URL/AUTH_SECRET (the three required vars) but never added the optional agent vars; ENCRYPTION_SECRET was set once then removed when re-deploying; REDIS_URL points at a Redis that is not started; Turborepo 2.x strict env dropped the var because it isn't declared in turbo.json.
Related errors
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/43d701c22c385719.
Report an issue: GitHub.