Mintplex-Labs/anything-llm · critical · Error
Cannot create JWT as JWT_SECRET is unset.
Error message
Cannot create JWT as JWT_SECRET is unset.
What it means
Thrown by makeJWT() when process.env.JWT_SECRET is falsy. The function signs JWTs for sessions and API tokens; without a secret, signing is impossible and would silently produce an insecure token, so it refuses. AnythingLLM expects JWT_SECRET to be set in multi-user mode; in single-user mode it may also be required for system features.
Source
Thrown at server/utils/http/index.js:27
function reqBody(request) {
return typeof request.body === "string"
? JSON.parse(request.body)
: request.body;
}
function queryParams(request) {
return request.query;
}
/**
* Creates a JWT with the given info and expiry
* @param {object} info - The info to include in the JWT
* @param {string} expiry - The expiry time for the JWT (default: 30 days)
* @returns {string} The JWT
*/
function makeJWT(info = {}, expiry = "30d") {
if (!process.env.JWT_SECRET)
throw new Error("Cannot create JWT as JWT_SECRET is unset.");
return JWT.sign(info, process.env.JWT_SECRET, { expiresIn: expiry });
}
/**
* Gets the user from the session
* Note: Only valid for multi-user mode
* as single-user mode with password is not a "user"
* @param {import("express").Request} request - The request object
* @param {import("express").Response} response - The response object
* @returns {Promise<import("@prisma/client").users | null>} The user
*/
async function userFromSession(request, response = null) {
if (!!response && !!response.locals?.user) {
return response.locals.user;
}
const auth = request.header("Authorization");
const token = auth ? auth.split(" ")[1] : null;View on GitHub (pinned to 526360e320)
Solutions
- Set JWT_SECRET in .env to a long random string (e.g. `openssl rand -hex 32`).
- For Docker, pass -e JWT_SECRET=... or mount the env file.
- Add a startup check that fails fast with a clear message if JWT_SECRET is missing.
- Re-issue existing sessions/tokens after rotating the secret, since old tokens become invalid.
Example fix
// before
// .env has no JWT_SECRET -> makeJWT throws on first login
// after
// .env
JWT_SECRET=9f2c...long-random-hex...
// server bootstrap
if (!process.env.JWT_SECRET && MULTI_USER_MODE)
throw new Error('JWT_SECRET is required in multi-user mode'); Defensive patterns
Strategy: validation
Validate before calling
if (!process.env.JWT_SECRET || process.env.JWT_SECRET.length < 16)
throw new Error('JWT_SECRET must be set to a strong random string'); Type guard
function hasJwtSecret(): boolean {
return typeof process.env.JWT_SECRET === 'string' && process.env.JWT_SECRET.length > 0;
} Try / catch
try {
const token = makeJWT(info, expiry);
} catch (e) {
if (e.message.includes('JWT_SECRET is unset'))
return res.status(503).json({ error: 'Server auth not configured' });
throw e;
} Prevention
- Set JWT_SECRET via `openssl rand -hex 32` in .env and in Docker env.
- Add a startup assertion so the server fails fast if missing.
- Rotate the secret with a redeploy; old tokens become invalid.
When it happens
Trigger: Booting the server without JWT_SECRET in .env; a Docker container missing the env var; running in an environment where the .env file wasn't loaded (wrong NODE_ENV path). Any login, session creation, or API-key issuance path that calls makeJWT.
Common situations: Fresh install that skipped the env bootstrap; a deployment that rotated secrets and forgot to redeploy; CI running without the secret; switching NODE_ENV so a different .env file is loaded.
Related errors
- No xAI API key was set.
- No Z.AI API key was set.
- No Azure API key was set.
- No Cohere API key was set.
- No Gemini API key was set.
AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13).
Data as JSON: /api/errors/ccbb992e69ab2d04.
Report an issue: GitHub.