{"record":{"id":"eb573274ef91ff51","repo":"Mintplex-Labs/anything-llm","slug":"device-not-found-eb5732","errorCode":null,"errorMessage":"Device not found","messagePattern":"Device not found","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/endpoints/mobile/middleware/index.js","lineNumber":23,"sourceCode":"/**\n * Validates the device id from the request headers by checking if the device\n * exists in the database and is approved.\n * @param {import(\"express\").Request} request\n * @param {import(\"express\").Response} response\n * @param {import(\"express\").NextFunction} next\n */\nasync function validDeviceToken(request, response, next) {\n  try {\n    const token = request.header(\"x-anythingllm-mobile-device-token\");\n    if (!token)\n      return response.status(400).json({ error: \"Device token is required\" });\n\n    const device = await MobileDevice.get(\n      { token: String(token) },\n      { user: true }\n    );\n    if (!device)\n      return response.status(400).json({ error: \"Device not found\" });\n    if (!device.approved)\n      return response.status(400).json({ error: \"Device not approved\" });\n\n    // If the device is associated with a user then we can associate it with the locals\n    // so we can reuse it later.\n    if (device.user) {\n      if (device.user.suspended)\n        return response.status(400).json({ error: \"User is suspended.\" });\n      response.locals.user = device.user;\n    }\n\n    delete device.user;\n    response.locals.device = device;\n    next();\n  } catch (error) {\n    console.error(\"validDeviceToken\", error);\n    response.status(500).json({ error: \"Invalid middleware response\" });\n  }","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/endpoints/mobile/middleware/index.js#L5-L41","documentation":"validDeviceToken resolves the header token with MobileDevice.get({ token }); if no desktop_mobile_devices row has that token it responds 400 { error: 'Device not found' }. On these routes devices are identified by their uuid v4 token (issued at /mobile/register and stored in the DB), not by id.","triggerScenarios":"Using a token whose device row the admin deleted (DELETE /api/mobile/:id); a token truncated or altered in storage/transit; pointing the app at a different backend instance or database that never registered the device.","commonSituations":"Device revoked by admin while the phone still caches the token; server DB reset or restore wiping desktop_mobile_devices; switching the app between local and hosted AnythingLLM instances.","solutions":["Re-register: admin generates fresh connect-info/QR, app POSTs /mobile/register, store the new token","If the device should still exist, compare the stored token against the DB row (SELECT * FROM desktop_mobile_devices WHERE token = '...')","After any DB reset, expect all previously issued device tokens to be dead"],"exampleFix":"// before\nconst res = await api.command('workspaces');\nif (!res.ok) throw new Error(res.status);\n\n// after\nconst res = await api.command('workspaces');\nif (res.status === 400 && (await res.json()).error === 'Device not found') {\n  await Storage.remove('deviceToken');\n  return navigation.reset('PairScreen'); // re-register flow\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await api.command('workspaces');\n} catch (e) {\n  if (e.status === 400 && e.body?.error === 'Device not found') {\n    await clearStoredToken();\n    return startPairingFlow(); // fallback: re-register for a fresh token\n  }\n  throw e;\n}","preventionTips":["Keep the token in durable storage keyed per server base URL","Never reuse a token across instances or after a DB restore","Handle 'Device not found' as a distinct branch that resets pairing state"],"tags":["mobile","auth","device-token","revoked","not-found"],"backgroundTag":"invalid-api-token","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}