jackwener/OpenCLI · error · CliError

API_ERROR

API_ERROR

Error message

API_ERROR: ${data.errmsg ?? `WeRead API error ${data.errcode}`}

What it means

The WeRead CLI's fetchPrivateApi wraps every authenticated WeRead API call. When the HTTP response is OK but the JSON body carries a non-zero errcode, the adapter normalizes it into a CliError with code API_ERROR so callers get one consistent error type for upstream API rejections. The message is the server-provided errmsg, or a fallback naming the numeric errcode when the server omits one.

Source

Thrown at clis/weread/utils.js:177

    }
    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)
        return [];
    const rawBookIdSet = new Set(rawBookIds);
    const projectedIndexedBookIds = Array.from(new Set(snapshot.shelfIndexes
        .filter((entry) => Number.isFinite(entry?.idx))
        .sort((left, right) => Number(left?.idx ?? Number.MAX_SAFE_INTEGER) - Number(right?.idx ?? Number.MAX_SAFE_INTEGER))
        .map((entry) => String(entry?.bookId || '').trim())

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log out and back in to weread.qq.com in Chrome to refresh the session cookies, then retry
  2. Print the raw body (data.errcode/errmsg) to see which upstream error code is being returned and consult WeRead's error semantics
  3. Re-check the arguments passed to the API call (bookId, path, query params) for typos or stale IDs
  4. Retry after a delay in case of transient WeRead-side failures
  5. If an errcode appears consistently, update the CLI to handle the new upstream error code

Example fix

// before
const data = await fetchPrivateApi('shelf/friendCommon', { user });
// after
try {
    const data = await fetchPrivateApi('shelf/friendCommon', { user });
} catch (e) {
    if (e.code === 'API_ERROR') console.error(`WeRead rejected the request: ${e.message} (re-login if AUTH_REQUIRED persists)`);
    throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// best pre-check: ensure login cookies exist before calling
if (!hasWeReadCookies()) throw new Error('Log in to weread.qq.com in Chrome first');

Type guard

function isApiError(e) { return e instanceof Error && e.code === 'API_ERROR'; }

Try / catch

try {
    const data = await fetchPrivateApi(path, params);
} catch (e) {
    if (e.code === 'API_ERROR') {
        // e.message is upstream errmsg or `WeRead API error <errcode>`; re-auth may fix it
    } else if (e.code === 'AUTH_REQUIRED') {
        // prompt login flow
    } else throw e;
}

Prevention

When it happens

Trigger: Any fetchPrivateApi call (via the data/result helpers) where resp.ok is true but the response body has data.errcode set to a non-zero value, e.g. an expired session cookie, invalid bookId, or a WeRead-side business rejection.

Common situations: Session cookies from Chrome have expired or been rotated; querying a bookId the account cannot access; WeRead changes an endpoint's error contract and starts returning errcode in a 200 response; transient WeRead service degradation.

Related errors


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