actualbudget/actual · error
Invalid token_expiration value: ${val}: value was "${val}"
Error message
Invalid token_expiration value: ${val}: value was "${val}" What it means
Thrown by the token_expiration validator in load-config.js during server startup. The value must be 'never', 'openid-provider', a finite non-negative number, or a string coercible to such a number; anything else (e.g. '30d', 'abc', negative numbers) aborts config loading with this message.
Source
Thrown at packages/sync-server/src/load-config.js:43
path.dirname(require.resolve('@actual-app/web/package.json')),
'build',
);
debug(`Actual web build path: '${actualAppWebBuildPath}'`);
// Custom formats
convict.addFormat({
name: 'tokenExpiration',
validate(val) {
if (val === 'never' || val === 'openid-provider') return;
if (typeof val === 'number' && Number.isFinite(val) && val >= 0) return;
// Handle string values that can be converted to numbers (from env vars)
if (typeof val === 'string') {
const numVal = Number(val);
if (Number.isFinite(numVal) && numVal >= 0) return;
}
throw new Error(
`Invalid token_expiration value: ${val}: value was "${val}"`,
);
},
coerce(val) {
if (val === 'never' || val === 'openid-provider') return val;
if (typeof val === 'number') return val;
// Convert string values to numbers for environment variables
if (typeof val === 'string') {
const numVal = Number(val);
if (Number.isFinite(numVal) && numVal >= 0) return numVal;
}
return val; // Let validate() handle invalid values
},
});
// Main config schemaView on GitHub (pinned to d4334cb6e6)
Solutions
- Set TOKEN_EXPIRATION to a plain number of days (e.g. '60') — only integers/floats >= 0 are accepted.
- Use the special literals 'never' or 'openid-provider' if you want no expiry / provider-controlled expiry.
- Convert duration expressions yourself: '30d' -> '30'.
- Check for stray quotes, spaces, or CR characters in the env value if a numeric-looking value still fails.
Example fix
// before TOKEN_EXPIRATION=30d // throws 'Invalid token_expiration value: 30d' // after TOKEN_EXPIRATION=30
Defensive patterns
Strategy: validation
Validate before calling
const raw = process.env.TOKEN_EXPIRATION;
const isValid = raw === undefined || raw === 'never' || raw === 'openid-provider' ||
(Number.isFinite(Number(raw)) && Number(raw) >= 0);
if (!isValid) throw new Error(`TOKEN_EXPIRATION="${raw}" is invalid; use a non-negative number, 'never', or 'openid-provider'`); Type guard
function isTokenExpiration(v: unknown): v is 'never' | 'openid-provider' | number {
if (v === 'never' || v === 'openid-provider') return true;
if (typeof v === 'number') return Number.isFinite(v) && v >= 0;
if (typeof v === 'string') { const n = Number(v); return Number.isFinite(n) && n >= 0; }
return false;
} Try / catch
try {
const config = loadConfig(configDir);
startServer(config);
} catch (err) {
if (err instanceof Error && err.message.includes('Invalid token_expiration')) {
console.error('Fix TOKEN_EXPIRATION env var: number of days, or never/openid-provider');
process.exit(1);
}
throw err;
} Prevention
- Only use plain numbers, 'never', or 'openid-provider' for TOKEN_EXPIRATION.
- Never write durations like '30d' or '1h' into the env var.
- Validate env vars in CI or a startup wrapper before launching the server.
- Check the official docs/example .env for accepted formats.
When it happens
Trigger: Setting TOKEN_EXPIRATION env var to a non-numeric string like '30d', '-1', '1h', or leaving whitespace/garbage in the env; putting an invalid default in the config file.
Common situations: Admins assuming human-readable durations ('24h', '7d') are supported; copying Docker env examples with placeholder text; typos like 'never ' with trailing space handled, but 'expired' etc. not.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid config file: expected an object with keys: ${configF
- Invalid config file: unknown key "${key}"
- Invalid config file: key "${key}" must be a string, got ${ty
- Invalid config file: key "${key}" must be a non-negative int
- Invalid config file: key "${key}" must be a boolean, got ${t
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/5b0c0fa32c847f04.
Report an issue: GitHub.