amruthpillai/reactive-resume · critical · ORPCError
PRECONDITION_FAILED
PRECONDITION_FAILED
Error message
AI providers are unavailable because ENCRYPTION_SECRET is not configured.
What it means
PRECONDITION_FAILED thrown by throwCredentialEncryptionUnavailable. Reached when an AI feature's catch block detects the sentinel error message 'AI_CREDENTIAL_ENCRYPTION_UNAVAILABLE' (isCredentialEncryptionUnavailable), which assertCredentialEncryptionConfigured raises when the server lacks the ENCRYPTION_SECRET needed to decrypt stored API keys. AI features cannot run because credentials cannot be read.
Source
Thrown at packages/api/src/features/ai/router.ts:36
function isAiProviderGatewayError(error: unknown): boolean {
return error instanceof AISDKError;
}
function isCredentialEncryptionUnavailable(error: unknown): boolean {
return error instanceof Error && error.message === "AI_CREDENTIAL_ENCRYPTION_UNAVAILABLE";
}
/** Throws a BAD_GATEWAY ORPCError, preserving the original cause for upstream error reporters. */
function throwAiProviderGatewayError(cause?: unknown): never {
throw new ORPCError("BAD_GATEWAY", { message: "Could not reach the AI provider.", cause });
}
function throwAiProviderConfigError(): never {
throw new ORPCError("BAD_REQUEST", { message: "Invalid AI provider configuration." });
}
function throwCredentialEncryptionUnavailable(): never {
throw new ORPCError("PRECONDITION_FAILED", {
message: "AI providers are unavailable because ENCRYPTION_SECRET is not configured.",
});
}
function throwResumeStructureError(error: ZodError): never {
throw new ORPCError("BAD_REQUEST", {
message: "Invalid resume data structure",
cause: flattenError(error),
});
}
async function getRunnableProvider(userId: string, aiProviderId?: string) {
const provider = aiProviderId
? await aiProvidersService.getRunnableById({ id: aiProviderId, userId })
: await aiProvidersService.getDefaultRunnable({ userId });
if (!provider) throw new ORPCError("BAD_REQUEST", { message: "No tested AI provider is available." });
View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Set ENCRYPTION_SECRET in the server environment to a stable, sufficiently random value and restart.
- Keep ENCRYPTION_SECRET constant across restarts — changing it invalidates previously encrypted API keys.
- Add a startup check / health probe that fails fast when ENCRYPTION_SECRET is absent.
- After setting it, re-enter API keys for existing providers (if the secret changed they will be undecryptable).
Example fix
# before # .env has no ENCRYPTION_SECRET # after ENCRYPTION_SECRET=$(openssl rand -hex 32)
Defensive patterns
Strategy: validation
Validate before calling
function assertEncryptionSecretConfigured() {
if (!process.env.ENCRYPTION_SECRET) throw new Error('ENCRYPTION_SECRET missing');
} Type guard
function isEncryptionUnavailable(e) {
return e?.code === 'PRECONDITION_FAILED' && /ENCRYPTION_SECRET/i.test(e.message);
} Try / catch
try {
await ai.parsePdf(input);
} catch (e) {
if (e?.code === 'PRECONDITION_FAILED' && /ENCRYPTION_SECRET/i.test(e.message)) {
showAdminNotice('Set ENCRYPTION_SECRET on the server to enable AI.');
} else throw e;
} Prevention
- Set ENCRYPTION_SECRET in every environment before enabling AI.
- Add a startup health check that fails when the secret is missing.
- Keep the secret stable to avoid invalidating encrypted API keys.
When it happens
Trigger: Any AI feature invocation on a deployment where ENCRYPTION_SECRET is missing or empty, so decryptCredential/encryptCredential refuse to operate and the service throws the sentinel before reading any provider.
Common situations: Fresh deployment that did not set ENCRYPTION_SECRET; secret rotation that left the env var unset; container restart after a config change dropped the secret; the provider list itself returns a PRECONDITION_FAILED from the same root cause.
Related errors
- BAD_REQUEST
- The model returned too much text during the provider test.
- BAD_REQUEST
- PRECONDITION_FAILED
- An unknown error occurred while validating the merged resume
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/ae21b68461e6fbfb.
Report an issue: GitHub.