jackwener/OpenCLI · error · CliError

AUTH_REQUIRED

AUTH_REQUIRED

Error message

AUTH_REQUIRED: Not logged in to WeRead

What it means

Thrown by fetchPrivateApi when the private WeRead API indicates the session is not authenticated: either an HTTP 401 status or a JSON body whose errcode is in {-2010, -2012} (WeRead's auth error codes), detected by isAuthErrorResponse. Thrown as CliError code AUTH_REQUIRED with the remediation hint to log in to weread.qq.com in Chrome first, because cookies for the request are extracted from the browser profile.

Source

Thrown at clis/weread/utils.js:171

                'User-Agent': WEREAD_UA,
                'Origin': 'https://weread.qq.com',
                'Referer': 'https://weread.qq.com/',
                ...(cookieHeader ? { 'Cookie': cookieHeader } : {}),
            },
        });
    }
    catch (error) {
        throw new CliError('FETCH_ERROR', `Failed to fetch ${path}: ${error instanceof Error ? error.message : String(error)}`, 'WeRead API may be temporarily unavailable');
    }
    let data;
    try {
        data = await resp.json();
    }
    catch {
        throw new CliError('PARSE_ERROR', `Invalid JSON response for ${path}`, 'WeRead may have returned an HTML error page');
    }
    if (isAuthErrorResponse(resp, data)) {
        throw new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first');
    }
    if (!resp.ok) {
        throw new CliError('FETCH_ERROR', `HTTP ${resp.status} for ${path}`, 'WeRead API may be temporarily unavailable');
    }
    if (data?.errcode != null && data.errcode !== 0) {
        throw new CliError('API_ERROR', data.errmsg ?? `WeRead API error ${data.errcode}`);
    }
    return data;
}
function getUniqueRawBookIds(snapshot) {
    return Array.from(new Set(snapshot.rawBooks
        .map((book) => String(book?.bookId || '').trim())
        .filter(Boolean)));
}
/** Mirror of hasTrustedIndexes in buildShelfSnapshotPollScript — keep in sync */
function getTrustedIndexedBookIds(snapshot) {
    const rawBookIds = getUniqueRawBookIds(snapshot);
    if (rawBookIds.length === 0)

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open https://weread.qq.com in Chrome, log in (scan QR with the WeChat/WeRead app), then rerun the CLI — this refreshes the extracted cookies.
  2. Verify the cookies being sent: check that wr_vid and wr_sso exist via page.getCookies({ domain: 'weread.qq.com' }).
  3. Confirm the CLI is pointed at the Chrome profile you actually logged in with (not a work/personal profile switch).
  4. Catch CliError code 'AUTH_REQUIRED' in scripts and pause/notify the user instead of retrying — retrying cannot fix an expired session.
  5. If sessions expire frequently, keep the Chrome tab logged in / disable 'clear cookies on exit' settings.

Example fix

// before
const highlights = await fetchPrivateApi(page, '/u/book/notes', params);
// after
try {
  const highlights = await fetchPrivateApi(page, '/u/book/notes', params);
} catch (e) {
  if (e.code === 'AUTH_REQUIRED') {
    console.error('Please log in to https://weread.qq.com in Chrome, then retry.');
    process.exitCode = 2;
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check auth cookies exist in the browser profile before private-API calls
const cookies = await page.getCookies({ domain: 'weread.qq.com' });
const loggedIn = ['wr_vid', 'wr_sso'].every(n => cookies.some(c => c.name === n));
if (!loggedIn) console.warn('Not logged in to WeRead — AUTH_REQUIRED expected; log in at https://weread.qq.com in Chrome');

Type guard

function isAuthRequiredError(e) {
  return e != null && typeof e === 'object' && e.code === 'AUTH_REQUIRED';
}

Try / catch

import { CliError } from '@jackwener/opencli/errors';
try {
  const data = await callWereadPrivateCommand();
} catch (e) {
  if (isAuthRequiredError(e)) {
    console.error('Not logged in to WeRead. Log in to https://weread.qq.com in Chrome, then rerun.');
    process.exitCode = 2; // do NOT retry — the session cannot self-heal
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Any fetchPrivateApi call where resp.status === 401 or Number(data.errcode) is -2010/-2012: expired wr_sso/wr_vid session cookies, a Chrome profile that was never logged in, cookies cleared by browser hygiene settings, or the user logging out in Chrome since the last run.

Common situations: WeRead session expiry (days after last browser login); switching Chrome profiles so the CLI reads cookies from a non-logged-in profile; clearing cookies/site data; cookie extraction from the wrong profile directory; using the CLI before ever logging in on that machine.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/8c49fe8565e6f575. Report an issue: GitHub.