{"record":{"id":"37ce8e1fbda53ef1","repo":"jackwener/OpenCLI","slug":"malformed-response-from-douyin-api-method-ur","errorCode":null,"errorMessage":"Malformed response from Douyin API (${method} ${url})","messagePattern":"Malformed response from Douyin API \\((.+?) (.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/browser-fetch.js","lineNumber":60,"sourceCode":"        clearTimeout(timer);\n      }\n    })()\n  `;\n    let result;\n    try {\n        result = unwrapEvaluateResult(await page.evaluate(js));\n    }\n    catch (error) {\n        throw new CommandExecutionError(`Douyin API request failed (${method} ${url}): ${error instanceof Error ? error.message : String(error)}`);\n    }\n    if (result == null) {\n        throw new CommandExecutionError(\n            `Empty response from Douyin API (${method} ${url})`,\n            'The endpoint may have been retired or may now require signed parameters.',\n        );\n    }\n    if (Array.isArray(result) || typeof result !== 'object') {\n        throw new CommandExecutionError(`Malformed response from Douyin API (${method} ${url})`);\n    }\n    if (result && typeof result === 'object' && 'status_code' in result) {\n        const code = result.status_code;\n        if (code !== 0) {\n            const msg = result.status_msg ?? result.message ?? 'unknown error';\n            if (isAuthLikeError(code, msg)) {\n                throw new AuthRequiredError('creator.douyin.com', `Douyin API auth/permission error ${code} at ${method} ${url}: ${msg}`);\n            }\n            throw new CommandExecutionError(`Douyin API error ${code} at ${method} ${url}: ${msg}`);\n        }\n    }\n    return result;\n}\n","sourceCodeStart":42,"sourceCodeEnd":74,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/browser-fetch.js#L42-L74","documentation":"browserFetch validates that the JSON returned by a Douyin API endpoint is a plain object. When the parsed body is an array or a non-object (string/number/null), it means the endpoint no longer returns the expected envelope, so the library throws this CommandExecutionError instead of letting downstream parsing fail confusingly.","triggerScenarios":"The requested endpoint returns HTML (redirect/login page), an array, a string, or any non-object JSON. Typical with retired endpoints, anti-bot interstitials, or when cookies are missing and the server responds with a non-JSON-shaped body.","commonSituations":"Douyin changed an API response shape after a site update; the browser session was redirected to a login/captcha page whose JSON parse yields a non-object; the caller pointed at the wrong URL; expired cookies cause a soft-redirect response.","solutions":["Re-authenticate the browser session on creator.douyin.com (log in again / refresh cookies) and retry.","Verify the endpoint URL is current and still returns the expected object envelope (curl it and inspect the body).","Log the raw response body before parsing to see what is actually being returned (HTML vs JSON).","Update the library or adjust the endpoint if Douyin changed the API response contract."],"exampleFix":"// before: assuming any JSON is fine\nconst data = await browserFetch(page, url);\nconsole.log(data.items);\n// after: catch and inspect\ntry {\n  const data = await browserFetch(page, url);\n  console.log(data.items);\n} catch (e) {\n  if (String(e.message).includes('Malformed response')) {\n    console.error('Session likely expired or endpoint retired; re-login to creator.douyin.com');\n  }\n  throw e;\n}","handlingStrategy":"type-guard","validationCode":"function isPlainObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }\nif (!isPlainObject(json)) throw new Error('endpoint returned non-object body — re-authenticate or check URL');","typeGuard":"function isDouyinEnvelope(v): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  const data = await browserFetch(page, url);\n} catch (e) {\n  if (String(e.message).startsWith('Malformed response from Douyin API')) {\n    await refreshSession(page); // dump body + re-login\n  }\n  throw e;\n}","preventionTips":["Verify the session is logged in before calling endpoints","Check response Content-Type is JSON before parsing","Keep endpoint URLs updated after Douyin site changes","Log raw bodies when debugging to spot redirect/HTML responses"],"tags":["douyin","api","response-validation","anti-bot"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}