{"record":{"id":"88447ccc014259d3","repo":"jackwener/OpenCLI","slug":"empty-response-from-douyin-api-method-url","errorCode":null,"errorMessage":"Empty response from Douyin API (${method} ${url})","messagePattern":"Empty response from Douyin API \\((.+?) (.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/_shared/browser-fetch.js","lineNumber":54,"sourceCode":"        } catch (error) {\n          return { status_code: res.ok ? -2 : res.status, status_msg: \\`JSON parse failed: \\${text.slice(0, 500) || String(error && error.message || error)}\\` };\n        }\n      } catch (error) {\n        return { status_code: -1, status_msg: String(error && error.message || error) };\n      } finally {\n        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;","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/_shared/browser-fetch.js#L36-L72","documentation":"browserFetch got a successful evaluate but the unwrapped result was null/undefined, meaning the Douyin endpoint returned an empty body. The library throws this CommandExecutionError with a hint that the endpoint may be retired or now requires signed parameters. It distinguishes 'request failed' from 'request succeeded but nothing came back'.","triggerScenarios":"page.evaluate returns null/undefined after unwrap — endpoint returns 204/empty body, response intercepted and stripped, or the in-page fetch resolved with no parseable JSON.","commonSituations":"Douyin retired an old public API; aweb-level changes requiring X-Bogus/_signature params that the script doesn't supply; geo/restriction returning empty payloads; logged-out session getting empty responses.","solutions":["Update the library to a version using the current signed Douyin API endpoints","Verify the endpoint still works by calling it manually in the browser console","Log in to Douyin in the controlled browser to restore full API access","Regenerate any required signed parameters (X-Bogus/msToken) before the request"],"exampleFix":"// before\nif (result == null) throw new CommandExecutionError(`Empty response from Douyin API (${method} ${url})`, ...);\n// after\nif (result == null) {\n  const status = await page.evaluate(() => fetch(location.href, {method:'HEAD'}).then(r=>r.status).catch(()=>-1));\n  if (status === 200) await refreshSignatures(page);\n  result = unwrapEvaluateResult(await page.evaluate(js));\n  if (result == null) throw new CommandExecutionError(`Empty response from Douyin API (${method} ${url})`);\n}","handlingStrategy":"validation","validationCode":"const raw = await page.evaluate(fetchJs);\nif (raw == null) throw new Error('Douyin endpoint returned empty body; check endpoint/signatures before parsing');","typeGuard":"function isNonEmptyObject(v) { return v != null && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0; }","tryCatchPattern":"try {\n  const data = await browserFetch(page, 'GET', url);\n} catch (e) {\n  if (/Empty response from Douyin API/.test(e.message)) {\n    await loginDouyin(page);\n    const data = await browserFetch(page, 'GET', url);\n  } else throw e;\n}","preventionTips":["Keep Douyin API endpoints and signing logic up to date","Maintain a logged-in Douyin session in the controlled browser","Test endpoints manually in the console after site updates","Detect empty payloads early and fall back to alternate endpoints"],"tags":["douyin","api","empty-response","signing"],"backgroundTag":"empty-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}