{"record":{"id":"afbe3c04b37bc63f","repo":"Zie619/n8n-workflows","slug":"could-not-find-video-url-in-the-json-data","errorCode":null,"errorMessage":"Could not find video URL in the JSON data.","messagePattern":"Could not find video URL in the JSON data\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workflows/Code/1802_Code_Manual_Import_Webhook.json","lineNumber":76,"sourceCode":"              \"name\": \"User-Agent\",\n              \"value\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/91.0.4472.124\"\n            }\n          ]\n        }\n      },\n      \"typeVersion\": 4.2,\n      \"notes\": \"This httpRequest node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"734a5304-f67f-4ace-a1da-0d268664452c\",\n      \"name\": \"Scrape raw video URL\",\n      \"type\": \"n8n-nodes-base.code\",\n      \"position\": [\n        480,\n        20\n      ],\n      \"parameters\": {\n        \"jsCode\": \"const html = $input.first().json.data;\\nconst headers = $input.first().json.headers || {};\\nconst cookies = headers['set-cookie'] || [];\\n\\nif (!html) {\\n  throw new Error(\\\"HTML body is undefined. Check the previous node's output.\\\");\\n}\\nconst regex = /<script id=\\\"__UNIVERSAL_DATA_FOR_REHYDRATION__\\\" type=\\\"application\\\\/json\\\">([\\\\s\\\\S]*?)<\\\\/script>/;\\nconst match = html.match(regex);\\n\\nif (match) {\\n  const jsonStr = match[1];\\n  const data = JSON.parse(jsonStr);\\n  const videoUrl = data?.__DEFAULT_SCOPE__?.[\\\"webapp.video-detail\\\"]?.itemInfo?.itemStruct?.video?.playAddr;\\n  if (!videoUrl) {\\n    throw new Error(\\\"Could not find video URL in the JSON data.\\\");\\n  }\\n  return [{ json: { videoUrl, cookies: cookies.join('; ') } }];\\n} else {\\n  throw new Error(\\\"Could not find __UNIVERSAL_DATA_FOR_REHYDRATION__ script in the HTML.\\\");\\n}\"\n      },\n      \"typeVersion\": 2,\n      \"notes\": \"This code node performs automated tasks as part of the workflow.\"\n    },\n    {\n      \"id\": \"f574ccb8-6f5f-4e55-a2d5-7ad775d3c4e5\",\n      \"name\": \"Output video file without watermark\",\n      \"type\": \"n8n-nodes-base.httpRequest\",\n      \"position\": [\n        900,\n        20\n      ],\n      \"parameters\": {\n        \"url\": \"{{ $env.BASE_URL }}\",\n        \"options\": {\n          \"response\": {\n            \"response\": {\n              \"responseFormat\": \"file\"","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/workflows/Code/1802_Code_Manual_Import_Webhook.json#L58-L94","documentation":"Thrown by the same 'Scrape raw video URL' node after it successfully found and parsed the __UNIVERSAL_DATA_FOR_REHYDRATION__ JSON blob in the TikTok page HTML. The parsed data is walked down __DEFAULT_SCOPE__ → 'webapp.video-detail' → itemInfo → itemStruct → video → playAddr; if that deep path yields nothing, the node refuses to continue.","triggerScenarios":"The TikTok page embedded valid hydration JSON but the video-detail scope is absent or shaped differently: the video is private, deleted, region-blocked, age-restricted, a photo post instead of a video, or TikTok served a login/bot-check page whose hydration blob has no webapp.video-detail key. It also fires when playAddr was renamed in a TikTok frontend update.","commonSituations":"Users submitting URLs to private/removed videos; TikTok A/B tests or frontend changes renaming playAddr; scraping from a datacenter IP triggering bot interstitials; the item being a slideshow (image post) with no video object.","solutions":["Verify the submitted video URL opens publicly in an incognito browser (not private, not deleted, not region-locked).","Log Object.keys(data.__DEFAULT_SCOPE__) and the shape under 'webapp.video-detail' to see which hop broke — itemInfo may contain an error status instead of itemStruct.","Add browser-like headers (User-Agent, Referer) and cookies to the upstream fetch to avoid bot-check pages.","If TikTok renamed the field, update the optional-chaining path to the new key; consider falling back to the itemStruct.video.downloadAddr or bitrateInfo playAddr variants."],"exampleFix":"// before\nconst videoUrl = data?.__DEFAULT_SCOPE__?.[\"webapp.video-detail\"]?.itemInfo?.itemStruct?.video?.playAddr;\nif (!videoUrl) {\n  throw new Error(\"Could not find video URL in the JSON data.\");\n}\n\n// after\nconst detail = data?.__DEFAULT_SCOPE__?.[\"webapp.video-detail\"];\nconst item = detail?.itemInfo?.itemStruct;\nconst videoUrl = item?.video?.playAddr\n  || item?.video?.bitrateInfo?.[0]?.PlayAddr?.UrlList?.[0]\n  || item?.video?.downloadAddr;\nif (!videoUrl) {\n  throw new Error(`Could not find video URL. itemInfo status: ${JSON.stringify(detail?.itemInfo?.itemWriteType ?? detail?.itemInfo)}`);\n}","handlingStrategy":"type-guard","validationCode":"const detail = data?.__DEFAULT_SCOPE__?.[\"webapp.video-detail\"];\nconst item = detail?.itemInfo?.itemStruct;\nconst videoUrl = item?.video?.playAddr || item?.video?.bitrateInfo?.[0]?.PlayAddr?.UrlList?.[0];","typeGuard":"function extractPlayAddr(d) {\n  const v = d?.__DEFAULT_SCOPE__?.['webapp.video-detail']?.itemInfo?.itemStruct?.video;\n  return v?.playAddr || v?.downloadAddr || v?.bitrateInfo?.[0]?.PlayAddr?.UrlList?.[0] || null;\n}","tryCatchPattern":null,"preventionTips":["Validate the video URL is public (not private/deleted/region-locked) before submitting.","Try multiple playAddr variants — TikTok exposes several URL fields.","Include the itemInfo status in error output to distinguish bot-checks from removed videos.","Expect TikTok DOM/JSON drift; centralize the extraction path so fixes touch one function."],"tags":["n8n","tiktok","web-scraping","dom-change","code-node"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}