{"record":{"id":"6dcd69751cb5bb87","repo":"jackwener/OpenCLI","slug":"invalid-json-from-requesturl-error-message","errorCode":null,"errorMessage":"invalid JSON from ${requestUrl}: ${error.message}","messagePattern":"invalid JSON from (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"clis/tiktok/utils.js","lineNumber":244,"sourceCode":"  }\n  return '';\n}\n\nasync function fetchJson(url) {\n  const requestUrl = new URL(url, ${JSON.stringify(TIKTOK_HOST)}).toString();\n  const res = await fetch(requestUrl, {\n    credentials: 'include',\n    headers: { accept: 'application/json,text/plain,*/*' },\n  });\n  const text = await res.text();\n  if (!res.ok) {\n    throw new Error('HTTP ' + res.status + ' from ' + requestUrl + ': ' + text.slice(0, 160));\n  }\n  if (!text.trim()) return {};\n  try {\n    return JSON.parse(text);\n  } catch (error) {\n    throw new Error('invalid JSON from ' + requestUrl + ': ' + (error instanceof Error ? error.message : String(error)));\n  }\n}\n\nfunction assertTikTokApiSuccess(data, label) {\n  if (!data || typeof data !== 'object') return;\n  const code = data.status_code ?? data.statusCode;\n  if (code === undefined || code === null || Number(code) === 0) return;\n  const message = cleanText(data.status_msg ?? data.statusMsg ?? data.message ?? data.msg ?? code, 240);\n  if (Number(code) === 8 || /auth|captcha|login|permission|unauthori[sz]ed|forbidden/i.test(message)) {\n    throw new Error('AUTH_REQUIRED: ' + label + ' API failed: ' + message);\n  }\n  throw new Error(label + ' API failed: ' + message);\n}\n\nfunction findUniversalData() {\n  const scripts = Array.from(document.querySelectorAll('script'));\n  for (const script of scripts) {\n    const text = script.textContent || '';","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/tiktok/utils.js#L226-L262","documentation":"The fetch helper JSON-parses the response body and, if JSON.parse throws, re-throws an Error naming the URL and the parser's message. It guards against TikTok returning HTML (login walls, captchas, error pages) or truncated bodies where JSON was expected.","triggerScenarios":"The endpoint returned 200 but a non-JSON body — typically an HTML login/captcha page, an empty-but-whitespace-only body is fine (returns {}), but any other non-JSON payload fails JSON.parse.","commonSituations":"Expired session redirects the API to the login HTML page; WAF serves a challenge page with 200; CDN error page returned instead of JSON; response cut off mid-body by proxy.","solutions":["Check the JSON.parse message for the exact syntax error position — '<' at position 0 means HTML was returned","Refresh TikTok session cookies; an HTML body usually means the session is no longer valid","Log the raw body once to see what is actually being served","Verify the URL wasn't redirected (check response.url) to a login page"],"exampleFix":"// before\nreturn JSON.parse(text);\n// after\nif (text.trimStart().startsWith('<')) {\n  throw new Error('received HTML instead of JSON (likely auth wall) from ' + requestUrl);\n}\nreturn JSON.parse(text);","handlingStrategy":"type-guard","validationCode":"const res = await fetch(url, { credentials: 'include' });\nconst text = await res.text();\nif (text.trimStart().startsWith('<')) throw new Error('got HTML (auth wall?) — refresh session');","typeGuard":"function isJsonString(s) { if (typeof s !== 'string') return false; try { JSON.parse(s); return true; } catch { return false; } }","tryCatchPattern":"try { return JSON.parse(text); } catch (e) {\n  if (text.trimStart().startsWith('<')) throw new AuthRequiredError('tiktok.com', 'HTML served instead of JSON');\n  throw e;\n}","preventionTips":["Inspect Content-Type before parsing","Treat '<'-leading bodies as auth walls and refresh cookies","Log one raw response when parsing fails","Handle empty bodies explicitly ({} case already exists)"],"tags":["json","parsing","network","tiktok"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}