jackwener/OpenCLI · warning · EmptyResultError
lichess user: Lichess user "${username}" returned empty payl
Error message
lichess user: Lichess user "${username}" returned empty payload. What it means
lichess user fetches a user profile and expects a JSON object body. If the payload is falsy or not an object, it throws EmptyResultError stating the user returned an empty payload. The command intentionally avoids rendering a row of nulls (silent fallback) and surfaces the problem instead.
Source
Thrown at clis/lichess/user.js:46
'seenAt',
'gamesAll',
'gamesWin',
'gamesLoss',
'gamesDraw',
'topPerfName',
'topPerfRating',
'topPerfGames',
'fideRating',
'country',
'bio',
'url',
],
func: async (args) => {
const username = requireUsername(args.username);
const url = `${LICHESS_BASE}/api/user/${encodeURIComponent(username)}`;
const body = await lichessFetch(url, 'lichess user');
if (!body || typeof body !== 'object') {
throw new EmptyResultError('lichess user', `Lichess user "${username}" returned empty payload.`);
}
// Lichess marks closed accounts with `disabled: true` and strips data.
// Surface as EmptyResultError instead of a row of nulls (silent-fallback).
if (body.disabled === true) {
throw new EmptyResultError('lichess user', `Lichess user "${username}" is closed/disabled.`);
}
const perfs = body.perfs && typeof body.perfs === 'object' ? body.perfs : {};
// Pick the perf with the most games (excluding puzzle/storm/racer ephemera).
const playablePerfs = Object.entries(perfs).filter(([k, v]) => v && typeof v === 'object' && !['puzzle', 'storm', 'racer', 'streak'].includes(k));
let topPerfName = null;
let topPerfRating = null;
let topPerfGames = null;
for (const [name, p] of playablePerfs) {
const games = typeof p.games === 'number' ? p.games : 0;
if (topPerfGames == null || games > topPerfGames) {
topPerfName = name;
topPerfGames = games;
topPerfRating = typeof p.rating === 'number' ? p.rating : null;View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the request — empty payloads are often transient
- Verify the username exists at lichess.org/@/<username> in a browser
- Check username spelling/casing (Lichess is case-insensitive but the account must exist)
- If the account is closed, expect the distinct 'closed/disabled' error instead
Example fix
// before const body = await lichessFetch(url, 'lichess user'); // after let body = await lichessFetch(url, 'lichess user'); if (!body || typeof body !== 'object') body = await lichessFetch(url, 'lichess user'); // retry once
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight reachability
const head = await fetch('https://lichess.org/api/user/ornicar');
if (!head.ok) console.warn('Lichess API unhealthy — expect empty payloads'); Type guard
const isUserProfile = (b) => b !== null && typeof b === 'object' && typeof b.username === 'string';
Try / catch
try {
await lichessUser(username);
} catch (e) {
if (e.name === 'EmptyResultError' && String(e.message).includes('empty payload')) {
await sleep(1000); /* retry once */
} else throw e;
} Prevention
- Retry transient empty responses before surfacing errors
- Validate usernames on lichess.org first
- Avoid middlewares/proxies that can truncate bodies
- Distinguish closed accounts (disabled:true) from empty payloads
When it happens
Trigger: Calling lichess user with a username that Lichess resolves to an empty/non-object body (proxy or cache stripping the body), a transient API response with no JSON, or validation passing but the endpoint returning null.
Common situations: Intermittent Lichess API/CDN issues; usernames with unusual casing resolved oddly; network middleware (proxy) truncating responses; user in the middle of username change.
Related errors
- lichess top: Lichess returned no leaderboard rows for perf "
- lichess user: Lichess user "${username}" is closed/disabled.
- Bilibili ${label} API returned a malformed payload
- devto/${id}
- NO_DATA
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/24a84f5e74fd861e.
Report an issue: GitHub.