Mintplex-Labs/anything-llm · error
Invalid or expired registration token
Error message
Invalid or expired registration token
What it means
validRegistrationToken calls MobileDevice.tempToken(token), which checks an in-memory Map (TemporaryMobileDeviceRequests). It returns null — yielding 400 { error: 'Invalid or expired registration token' } — when the token is unknown, expired (tokens live 3 minutes: expiresAt = createdAt + 3*60_000), or already consumed: a finally block deletes the entry on every lookup, so each temp token works exactly once. The Map also dies with the process on restart.
Source
Thrown at server/endpoints/mobile/middleware/index.js:64
* and associates the user with the token (if valid). Temporary token is consumed
* and cannot be used again after this middleware is called.
* @param {*} request
* @param {*} response
* @param {*} next
*/
async function validRegistrationToken(request, response, next) {
try {
const authHeader = request.header("Authorization");
const tempToken = authHeader ? authHeader.split(" ")[1] : null;
if (!tempToken)
return response
.status(400)
.json({ error: "Registration token is required" });
const tempTokenData = MobileDevice.tempToken(tempToken);
if (!tempTokenData)
return response
.status(400)
.json({ error: "Invalid or expired registration token" });
// If in multi-user mode, we need to validate the user id
// associated exists, is not banned and then associate with locals so we can reuse it later.
// If not in multi-user mode then simply having a valid token is enough.
const multiUserMode = await SystemSettings.isMultiUserMode();
if (multiUserMode) {
if (!tempTokenData.userId)
return response
.status(400)
.json({ error: "User id not found in registration token" });
const user = await User.get({ id: Number(tempTokenData.userId) });
if (!user) return response.status(400).json({ error: "User not found" });
if (user.suspended)
return response
.status(400)
.json({ error: "User is suspended - cannot register device" });
response.locals.user = user;View on GitHub (pinned to 3aec848f28)
Solutions
- Fetch fresh connect-info (GET /api/mobile/connect-info) or rescan the QR and register immediately, within 3 minutes
- Never retry register with the same temp token — always obtain a new one first
- Pin connect-info and register to the same server instance; temp tokens are in-process memory, not shared state
Example fix
// before — reusing a stale token
await register(tempToken); // 400 invalid or expired
await register(tempToken); // retry, still fails
// after — refresh token, then register once
const info = await (await fetch('/api/mobile/connect-info')).json();
const fresh = new URL(info.connectionUrl).searchParams.get('t');
await register(fresh); Defensive patterns
Strategy: retry
Validate before calling
const TOKEN_TTL_MS = 3 * 60_000;
if (Date.now() - tokenFetchedAt > TOKEN_TTL_MS - 5_000) {
tempToken = await fetchFreshConnectInfo(); // avoid expired-token 400
} Try / catch
try {
await register(tempToken);
} catch (e) {
if (e.status === 400 && e.body?.error?.includes('Invalid or expired registration token')) {
const fresh = await fetchFreshConnectInfo(); // one retry with a new token
return register(fresh);
}
throw e;
} Prevention
- Register immediately after obtaining connect-info — the window is 3 minutes
- Never retry with the same temp token; it is single-use
- Keep connect-info and register on the same server process; tokens are in-memory only
When it happens
Trigger: Scanning the QR or copying the connect-info URL and registering more than 3 minutes later; retrying /mobile/register after a prior attempt (the first call consumed the token); server restart or hot-reload between connect-info and register; token typo; load balancer routing register to a different instance than the one holding the Map.
Common situations: Manual typing of the pairing URL in dev; nodemon/PM2 restart wiping in-memory state; user rescans a new QR but the app caches the old URL; horizontally scaled deployments.
Related errors
- Device OS and name are required
- Invalid device OS - ${deviceOs}
- Registration token is required
- User id not found in registration token
- User not found
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/9443b027f4449fa4.
Report an issue: GitHub.