{"record":{"id":"2f7e772ecb359294","repo":"jackwener/OpenCLI","slug":"lichess-user-lichess-user-username-is-closed","errorCode":null,"errorMessage":"lichess user: Lichess user \"${username}\" is closed/disabled.","messagePattern":"lichess user: Lichess user \"(.+?)\" is closed/disabled\\.","errorType":"error_code","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/lichess/user.js","lineNumber":51,"sourceCode":"        'topPerfName',\n        'topPerfRating',\n        'topPerfGames',\n        'fideRating',\n        'country',\n        'bio',\n        'url',\n    ],\n    func: async (args) => {\n        const username = requireUsername(args.username);\n        const url = `${LICHESS_BASE}/api/user/${encodeURIComponent(username)}`;\n        const body = await lichessFetch(url, 'lichess user');\n        if (!body || typeof body !== 'object') {\n            throw new EmptyResultError('lichess user', `Lichess user \"${username}\" returned empty payload.`);\n        }\n        // Lichess marks closed accounts with `disabled: true` and strips data.\n        // Surface as EmptyResultError instead of a row of nulls (silent-fallback).\n        if (body.disabled === true) {\n            throw new EmptyResultError('lichess user', `Lichess user \"${username}\" is closed/disabled.`);\n        }\n        const perfs = body.perfs && typeof body.perfs === 'object' ? body.perfs : {};\n        // Pick the perf with the most games (excluding puzzle/storm/racer ephemera).\n        const playablePerfs = Object.entries(perfs).filter(([k, v]) => v && typeof v === 'object' && !['puzzle', 'storm', 'racer', 'streak'].includes(k));\n        let topPerfName = null;\n        let topPerfRating = null;\n        let topPerfGames = null;\n        for (const [name, p] of playablePerfs) {\n            const games = typeof p.games === 'number' ? p.games : 0;\n            if (topPerfGames == null || games > topPerfGames) {\n                topPerfName = name;\n                topPerfGames = games;\n                topPerfRating = typeof p.rating === 'number' ? p.rating : null;\n            }\n        }\n        const counts = body.count && typeof body.count === 'object' ? body.count : {};\n        const profile = body.profile && typeof body.profile === 'object' ? body.profile : {};\n        return [{","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/lichess/user.js#L33-L69","documentation":"This EmptyResultError is thrown when the Lichess API reports the requested account as closed or disabled (`disabled: true`). The library deliberately converts this into an EmptyResultError rather than rendering a row of nulls, which would look like a silent fallback. It means the user exists (or existed) but the account is no longer active.","triggerScenarios":"Calling the `lichess user` command (via `user()`) for a username whose Lichess API response body contains `disabled: true`. This happens after the payload passes the non-empty object check, so the API did respond but with a deactivated account.","commonSituations":"Looking up a player who closed or was banned on Lichess; querying stale usernames from an old dataset or ratings list; typos that coincidentally match a disabled account handle.","solutions":["Verify the account status on lichess.org — if it is closed, the data is intentionally unavailable","Handle EmptyResultError in your caller and treat it as 'account deactivated', not as a network failure","If this is unexpected, double-check the username spelling for a close-but-active account","Use a different data source (e.g. archived data) if historical info about the closed account is needed"],"exampleFix":"// before\nconst user = await user(username); // throws on disabled accounts\n// after\nlet user;\ntry { user = await user(username); }\ncatch (e) {\n  if (e instanceof EmptyResultError) { console.warn('account closed/disabled'); user = null; }\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Cannot pre-validate account status without the API call itself.\n// Best pre-check: confirm the handle exists and is active via a HEAD/GET before processing.\nconst res = await fetch(`https://lichess.org/api/user/${encodeURIComponent(name)}`);\nconst body = await res.json();\nif (body?.disabled === true) return null; // account closed","typeGuard":"function isActiveAccount(body) {\n  return typeof body === 'object' && body !== null && body.disabled !== true;\n}","tryCatchPattern":"try {\n  const profile = await user(name);\n} catch (e) {\n  if (e instanceof EmptyResultError) {\n    console.warn(`Account \"${name}\" is closed/disabled; skipping.`);\n  } else throw e;\n}","preventionTips":["Treat EmptyResultError from user lookups as 'account deactivated', not a bug","Filter username lists against known-closed accounts before batch processing","Log the username alongside the error so batch jobs can report skipped accounts","Check lichess.org profile page when a lookup unexpectedly fails"],"tags":["lichess","api","account-disabled","empty-result"],"backgroundTag":"account-closed-or-disabled","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}