coleam00/Archon · critical

BETTER_AUTH_SECRET must be at least ${String(MIN_BETTER_AUTH

Error message

BETTER_AUTH_SECRET must be at least ${String(MIN_BETTER_AUTH_SECRET_LENGTH)} characters when web auth is enabled. Generate one with: openssl rand -base64 32

What it means

assertWebAuthAtBoot runs during startServer and refuses to boot with web authentication enabled but a BETTER_AUTH_SECRET shorter than the minimum length (MIN_BETTER_AUTH_SECRET_LENGTH). Better Auth signs session tokens with this secret; a weak/guessable key would let attackers forge sessions, so the server fails fast with remediation instructions instead of silently mounting auth on a weak key.

Source

Thrown at packages/server/src/auth/config.ts:39

/**
 * Web auth is active only when a Postgres connection AND a signing secret are
 * configured. SQLite installs (no DATABASE_URL) are always opted out.
 */
export function isWebAuthEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
  return Boolean(env.DATABASE_URL && env.BETTER_AUTH_SECRET);
}

/**
 * Fail fast at server boot: when web auth is enabled, the signing secret must be
 * long enough to be a real secret. A short/placeholder secret would let an
 * attacker forge sessions, so we throw with an actionable hint rather than
 * silently mounting auth on a weak key.
 */
export function assertWebAuthAtBoot(env: NodeJS.ProcessEnv = process.env): void {
  if (!isWebAuthEnabled(env)) return;
  const secret = env.BETTER_AUTH_SECRET ?? '';
  if (secret.length < MIN_BETTER_AUTH_SECRET_LENGTH) {
    throw new Error(
      `BETTER_AUTH_SECRET must be at least ${String(MIN_BETTER_AUTH_SECRET_LENGTH)} characters ` +
        'when web auth is enabled. Generate one with: openssl rand -base64 32'
    );
  }
}

/**
 * Parse the signup allowlist from `ARCHON_AUTH_ALLOWED_EMAILS` (comma-separated,
 * lowercased, trimmed, blanks dropped). An empty/unset list does NOT mean open
 * signup — see `getSignupMode` (empty defaults to `disabled` unless
 * `ARCHON_AUTH_OPEN_SIGNUP=true`).
 */
export function parseAllowedEmails(env: NodeJS.ProcessEnv = process.env): string[] {
  return (env.ARCHON_AUTH_ALLOWED_EMAILS ?? '')
    .split(',')
    .map(e => e.trim().toLowerCase())
    .filter(Boolean);
}

View on GitHub (pinned to 0773b97458)

Solutions

  1. Generate a strong secret with `openssl rand -base64 32` and set it as BETTER_AUTH_SECRET in the server environment/.env
  2. Verify the full secret was pasted (no truncation/whitespace) and its length meets the minimum
  3. If web auth is not needed, disable it so the check is skipped

Example fix

// before (.env)
BETTER_AUTH_SECRET=changeme
// after
# openssl rand -base64 32
BETTER_AUTH_SECRET=kJ8fQ2mN7xR4vT9wLpZ3aB6cD1eF0gH5iJ2kL3mN4oP5qR6sT7uV8wX9yZ0aB1c=
Defensive patterns

Strategy: validation

Validate before calling

const MIN = 32; // match MIN_BETTER_AUTH_SECRET_LENGTH
const secret = process.env.BETTER_AUTH_SECRET ?? '';
if (secret.length < MIN) {
  throw new Error(`BETTER_AUTH_SECRET must be >= ${MIN} chars: openssl rand -base64 32`);
}

Try / catch

try {
  await startServer();
} catch (err) {
  if (String(err.message).includes('BETTER_AUTH_SECRET')) {
    console.error('Set a strong BETTER_AUTH_SECRET before enabling web auth.');
  }
  throw err;
}

Prevention

When it happens

Trigger: startServer() → assertWebAuthAtBoot() with web auth enabled (per isWebAuthEnabled) and env.BETTER_AUTH_SECRET unset (treated as '') or shorter than the minimum length.

Common situations: Forgetting to set BETTER_AUTH_SECRET in a fresh deployment/.env; placeholder values like 'changeme' or 'secret'; a truncated secret from copy-paste; enabling web auth via a flag without adding the secret env var.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/7a6cd27a687b2db7. Report an issue: GitHub.