{"record":{"id":"0e3b18d9883e06a9","repo":"jackwener/OpenCLI","slug":"1point3acres-request-failed-http-res-status","errorCode":null,"errorMessage":"1point3acres request failed: HTTP ${res.status} ${res.statusText} from ${url}","messagePattern":"1point3acres request failed: HTTP (.+?) (.+?) from (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/1point3acres/utils.js","lineNumber":60,"sourceCode":"/** Fetch a GBK-encoded Discuz page and return decoded UTF-8 HTML. */\nexport async function fetchHtml(url, { headers = {}, cookie = '' } = {}) {\n    let res;\n    try {\n        res = await fetch(url, {\n            headers: {\n                'User-Agent': UA,\n                'Accept': 'text/html,application/xhtml+xml',\n                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',\n                ...(cookie ? { Cookie: cookie } : {}),\n                ...headers,\n            },\n            redirect: 'follow',\n        });\n    } catch (error) {\n        throw new CommandExecutionError(`1point3acres request failed: ${error?.message || error}`);\n    }\n    if (!res.ok) {\n        throw new CommandExecutionError(`1point3acres request failed: HTTP ${res.status} ${res.statusText} from ${url}`);\n    }\n    const buf = await res.arrayBuffer();\n    return new TextDecoder('gbk').decode(buf);\n}\n\n/** Pull cookie string from the live browser session for this domain.\n *  Discuz auth cookies (4Oaf_61d6_*, session) are HttpOnly and set on the\n *  root domain `.1point3acres.com`, so we need `getCookies` (not document.cookie)\n *  AND we need to query both host + root domain and merge.\n */\nexport async function getCookie(page) {\n    if (!page) return '';\n    const seen = new Map();\n    if (typeof page.getCookies === 'function') {\n        for (const opts of [{ domain: 'www.1point3acres.com' }, { domain: '.1point3acres.com' }]) {\n            try {\n                const cookies = await page.getCookies(opts);\n                for (const c of cookies || []) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/1point3acres/utils.js#L42-L78","documentation":"fetchHtml performs an HTTP GET (with redirect: 'follow') against a 1point3acres.com URL. If the response arrives but res.ok is false (any non-2xx status), it wraps the status line and URL into a CommandExecutionError so the caller knows the remote site rejected the request. Network-level failures (no response) are caught earlier and rethrown with a different message.","triggerScenarios":"Calling any 1point3acres-backed command whose fetchHtml request returns HTTP 4xx/5xx: e.g. the site returns 403 for blocked/scraping clients, 404 for a deleted thread, 429 for rate limiting, or 5xx during site outages.","commonSituations":"Site rate-limits or blocks the default User-Agent (403); requesting a deleted/moved thread (404); heavy scraping bursts triggering anti-bot protection; transient 1point3acres server errors; GBK-decoded pages being fetched during maintenance windows.","solutions":["Retry the command after a delay; the failure is often transient (429/5xx).","Check the status code embedded in the message: 403/429 means anti-bot blocking — slow down or change headers; 404 means the thread/URL no longer exists.","Verify the URL is a valid www.1point3acres.com page by opening it in a browser.","Log in via the browser session if the resource requires authentication, since guest access may be rejected."],"exampleFix":"// before\nconst html = await fetchHtml(url); // throws on 403/404/5xx\n// after\nlet html;\ntry {\n    html = await fetchHtml(url);\n} catch (e) {\n    if (/HTTP 429/.test(e.message)) await sleep(60_000); // back off on rate limit\n    else throw e;\n    html = await fetchHtml(url);\n}","handlingStrategy":"retry","validationCode":"// preflight: check reachability\nconst head = await fetch(url, { method: 'HEAD' });\nif (!head.ok) throw new Error(`Skipping: ${url} returned ${head.status}`);","typeGuard":"function isOkResponse(res) {\n    return res && typeof res.status === 'number' && res.status >= 200 && res.status < 300;\n}","tryCatchPattern":"try {\n    const html = await fetchHtml(url);\n} catch (e) {\n    if (/HTTP 429|HTTP 5\\d\\d/.test(e.message)) {\n        await new Promise(r => setTimeout(r, 60_000)); // back off and retry\n    } else {\n        console.error(`1point3acres fetch failed: ${e.message}`);\n    }\n}","preventionTips":["Throttle request rate to avoid 429/403 anti-bot responses","Reuse an authenticated browser session for protected pages","Validate URLs before fetching (must be 1point3acres pages that exist)","Handle the embedded HTTP status in the message to branch retry vs. give-up logic"],"tags":["http","network","http-status","scraping"],"backgroundTag":"http-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}