{"record":{"id":"b2eedcf265665d79","repo":"Mintplex-Labs/anything-llm","slug":"device-token-is-required","errorCode":null,"errorMessage":"Device token is required","messagePattern":"Device token is required","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/endpoints/mobile/middleware/index.js","lineNumber":16,"sourceCode":"const { MobileDevice } = require(\"../../../models/mobileDevice\");\nconst { SystemSettings } = require(\"../../../models/systemSettings\");\nconst { User } = require(\"../../../models/user\");\n\n/**\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","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/endpoints/mobile/middleware/index.js#L1-L34","documentation":"validDeviceToken middleware guards device-authenticated /api/mobile routes. It reads the x-anythingllm-mobile-device-token header; when the header is absent or empty it responds 400 { error: 'Device token is required' } before any handler runs. This is the long-lived device token issued by POST /mobile/register, not the temporary registration token.","triggerScenarios":"Calling a device-authenticated /api/mobile endpoint without the x-anythingllm-mobile-device-token header, sending it empty, misspelling it (e.g. underscores instead of dashes), or putting it in Authorization instead.","commonSituations":"Token not persisted after first registration; CORS preflight stripping custom x- headers because Access-Control-Allow-Headers omits it; reverse proxy (nginx) dropping custom headers by default.","solutions":["Set the header: 'x-anythingllm-mobile-device-token': '<token from /mobile/register>'","Check exact spelling — dashes, full lowercase, whole name","If behind a proxy/gateway, confirm custom x- headers are forwarded and allowed in CORS config"],"exampleFix":"// before\nfetch('/api/mobile/util', { method: 'POST', body: JSON.stringify(payload) });\n\n// after\nfetch('/api/mobile/util', {\n  method: 'POST',\n  headers: { 'x-anythingllm-mobile-device-token': deviceToken },\n  body: JSON.stringify(payload),\n});","handlingStrategy":"validation","validationCode":"if (!deviceToken) throw new Error('Device not paired — run registration first');\nconst headers = { 'x-anythingllm-mobile-device-token': deviceToken };","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize header injection in one API client wrapper so no call can forget it","Persist the device token immediately after /mobile/register succeeds","On CORS setups, verify the header survives preflight (Access-Control-Allow-Headers)"],"tags":["mobile","auth","headers","middleware","device-token"],"backgroundTag":"missing-auth-header","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}