{"record":{"id":"20e2f7751855da7d","repo":"pear-devs/pear-desktop","slug":"unauthorized","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":null,"httpStatus":401,"severity":"warning","filePath":"src/plugins/api-server/backend/main.ts","lineNumber":122,"sourceCode":"          alg: 'HS256',\n        })(ctx, next);\n      }\n      return await next();\n    };\n    this.app.use('/api/*', jwtGuard);\n    this.app.use('/api/*', async (ctx, next) => {\n      if (ctx.req.path.endsWith(`${API_VERSION}/ws`)) {\n        return await next();\n      }\n\n      const result = await JWTPayloadSchema.spa(await ctx.get('jwtPayload'));\n      const config = await backendCtx.getConfig();\n\n      const isAuthorized =\n        config.authStrategy === AuthStrategy.NONE ||\n        (result.success && config.authorizedClients.includes(result.data.id));\n      if (!isAuthorized) {\n        ctx.status(401);\n        return ctx.body('Unauthorized');\n      }\n\n      return await next();\n    });\n\n    // routes\n    registerControl(\n      this.app,\n      backendCtx,\n      () => this.songInfo,\n      () => this.currentRepeatMode,\n      () =>\n        backendCtx.window.webContents.executeJavaScript(\n          'document.querySelector(\"#like-button-renderer\")?.likeStatus',\n        ) as Promise<LikeType>,\n      () => this.volumeState,\n    );","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/pear-devs/pear-desktop/blob/1e2aac5706c870c93ed74ba8727ae8390d3b0fa5/src/plugins/api-server/backend/main.ts#L104-L140","documentation":"'Unauthorized' is the HTTP 401 body returned by the api-server's auth middleware when a client's request is not authorized. Authorization passes only when the configured authStrategy is NONE, or when client verification succeeded (result.success) AND the client's id is in config.authorizedClients. Otherwise the middleware sets status 401, returns 'Unauthorized', and short-circuits before the route handler runs.","triggerScenarios":"Any API request (GET/POST on any route) where the strategy is not NONE and either client verification failed (invalid/expired credentials, unknown client id) or the verified client id is not present in authorizedClients in the config.","commonSituations":"Client pairing not completed or pairing dialog rejected, client credentials deleted from authorizedClients, authStrategy changed from NONE to a real strategy without re-pairing clients, stale credentials after server restart, or a third-party tool hitting the API without credentials.","solutions":["Re-run the client pairing/authorization flow so the client gets valid credentials and is added to authorizedClients.","Verify config.authStrategy — if you intend open access locally, set it to AuthStrategy.NONE.","Check that the client is sending its credentials (headers/cookies as the strategy expects) on every request.","Inspect authorizedClients in the persisted config and add the client's id if verification succeeds but membership is missing."],"exampleFix":"// before (client request without credentials)\nawait fetch('http://localhost:10768/api/songs'); // 401 Unauthorized\n\n// after (complete pairing first, then send credentials)\n// pair the client once, e.g. via the auth/register endpoint, then:\nawait fetch('http://localhost:10768/api/songs', {\n  headers: { Authorization: `Bearer ${clientToken}` },\n});","handlingStrategy":"validation","validationCode":"// client-side: verify credentials are configured before making API calls\nif (authStrategy !== 'none' && !clientCredentials) {\n  throw new Error('Not paired with server — run registration first');\n}\nawait api.getSongs();","typeGuard":"const isUnauthorized = (res: Response): boolean => res.status === 401;","tryCatchPattern":"const res = await fetch(url, opts);\nif (res.status === 401) {\n  // re-pair or prompt user, then retry once\n  await reauthorize();\n  return fetch(url, opts);\n}","preventionTips":["Persist client credentials after pairing and attach them to every request.","Handle 401 centrally in the API client instead of per-endpoint.","Document that switching authStrategy away from NONE invalidates unpaired clients."],"tags":["api-server","http-401","authentication","middleware","unauthorized"],"backgroundTag":"http-401-unauthorized","analyzedSha":"1e2aac5706c870c93ed74ba8727ae8390d3b0fa5","analyzedAt":"2026-08-27T20:01:08.614Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}