{"record":{"id":"5bf5d80f2839d58c","repo":"jackwener/OpenCLI","slug":"xiaoyuzhou-api-rejected-the-credentials-with-servi","errorCode":null,"errorMessage":"Xiaoyuzhou API rejected the credentials with service code ${numericCode}","messagePattern":"Xiaoyuzhou API rejected the credentials with service code (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaoyuzhou/auth.js","lineNumber":243,"sourceCode":"    }\n    let parsed;\n    try {\n        parsed = JSON.parse(bodyText);\n    }\n    catch (error) {\n        throw new CommandExecutionError(`Xiaoyuzhou API returned invalid JSON: ${getErrorMessage(error)}`);\n    }\n    const serviceCode = parsed?.code;\n    if (serviceCode !== undefined && serviceCode !== null) {\n        const numericCode = Number(serviceCode);\n        if (!Number.isFinite(numericCode)) {\n            throw new CommandExecutionError('Xiaoyuzhou API returned an invalid service code');\n        }\n        if (numericCode === 401 || numericCode === 403) {\n            throw createXiaoyuzhouAuthError(`Xiaoyuzhou API rejected the credentials with service code ${numericCode}`);\n        }\n        if (numericCode !== 0 && numericCode !== 200) {\n            throw new CommandExecutionError(\n                parsed?.message || parsed?.msg || `Xiaoyuzhou API returned service code ${numericCode}`,\n            );\n        }\n    }\n    if (parsed?.success === false) {\n        throw new CommandExecutionError(parsed?.message || parsed?.msg || 'Xiaoyuzhou API returned success=false');\n    }\n    return {\n        credentials,\n        raw: parsed,\n        data: parsed?.data,\n    };\n}\n\nexport async function fetchXiaoyuzhouTranscriptBody(url, fetchImpl = fetch) {\n    let response;\n    try {\n        response = await fetchImpl(url, {","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaoyuzhou/auth.js#L225-L261","documentation":"When the Xiaoyuzhou API envelope returns service code 401 or 403, the CLI raises an authentication error via createXiaoyuzhouAuthError, indicating the stored credentials (token/cookies) were rejected. This distinguishes auth failures from generic service errors so users know to re-authenticate.","triggerScenarios":"Any requestXiaoyuzhouJson call (result, response, historyResponse, progressResponse, episodeResponse, transcriptResponse) where parsed.code is 401 or 403 — expired auth token, revoked session, missing/invalid Authorization header or cookies, or banned account.","commonSituations":"Token expired after the session lifetime passed; user logged out elsewhere invalidating the token; copied cookie/auth header incomplete or from a different account; clock skew invalidating signed tokens; account rate-limited into a 403.","solutions":["Re-authenticate: run the xiaoyuzhou auth/login flow to obtain fresh credentials, then retry.","Inspect stored credentials (token/cookies file) for truncation or staleness; replace them wholesale.","Verify the Authorization header/cookies are actually attached to the outgoing request.","If 403 persists with valid creds, check for account-level blocks (bans, region restrictions) or IP-based rate limiting."],"exampleFix":"// before: retrying with stale creds\nconst data = await requestXiaoyuzhouJson(credentials, '/episode/xyz', ...);\n// after: catch auth error and re-auth once\ntry {\n  data = await requestXiaoyuzhouJson(credentials, '/episode/xyz', ...);\n} catch (e) {\n  if (isXiaoyuzhouAuthError(e)) {\n    credentials = await refreshXiaoyuzhouCredentials();\n    data = await requestXiaoyuzhouJson(credentials, '/episode/xyz', ...);\n  } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// before calls: fail fast if credentials are missing/stale\nif (!credentials || !credentials.token || (credentials.expiresAt && Date.now() >= credentials.expiresAt)) {\n  credentials = await refreshXiaoyuzhouCredentials(); // re-auth before hitting the API\n}","typeGuard":"function isXiaoyuzhouAuthError(e) {\n  return e instanceof CommandExecutionError && /rejected the credentials with service code (401|403)/.test(e.message);\n}","tryCatchPattern":"try {\n  return await requestXiaoyuzhouJson(creds, path, params);\n} catch (e) {\n  if (isXiaoyuzhouAuthError(e)) {\n    const fresh = await reauthenticateXiaoyuzhou();\n    return await requestXiaoyuzhouJson(fresh, path, params); // retry once with new creds\n  }\n  throw e;\n}","preventionTips":["Store the token's expiry and refresh proactively before it lapses.","Wrap all xiaoyuzhou calls in one helper that auto-reauths once on 401/403.","Never hand-edit credential files; always use the auth flow to regenerate them.","Check machine clock sync if tokens are time-based.","Keep cookie/auth headers complete — copying partial values is a common cause."],"tags":["authentication","unauthorized","credentials","api"],"backgroundTag":"auth-rejected-401-403","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}