{"record":{"id":"2186320054f79dbe","repo":"denoland/deno","slug":"syntaxerror-218632","errorCode":"SyntaxError","errorMessage":"Failed to parse URL: ${scriptUrl}","messagePattern":"Failed to parse URL: (.+?)","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"runtime/js/99_main.js","lineNumber":400,"sourceCode":"      op_worker_maybe_wait_for_debugger();\n      dispatchWorkerMessage(syncData);\n    }\n  }\n}\n\nlet loadedMainWorkerScript = false;\n\nfunction importScripts(...urls) {\n  if (op_worker_get_type() !== \"classic\") {\n    throw new TypeError(\"Cannot import scripts in a module worker\");\n  }\n\n  const baseUrl = location.getLocationHref();\n  const parsedUrls = ArrayPrototypeMap(urls, (scriptUrl) => {\n    try {\n      return new url.URL(scriptUrl, baseUrl ?? undefined).href;\n    } catch {\n      throw new DOMException(\n        `Failed to parse URL: ${scriptUrl}`,\n        \"SyntaxError\",\n      );\n    }\n  });\n\n  // A classic worker's main script has looser MIME type checks than any\n  // imported scripts, so we use `loadedMainWorkerScript` to distinguish them.\n  // TODO(andreubotella) Refactor worker creation so the main script isn't\n  // loaded with `importScripts()`.\n  const scripts = op_worker_sync_fetch(\n    parsedUrls,\n    !loadedMainWorkerScript,\n  );\n  loadedMainWorkerScript = true;\n\n  for (let i = 0; i < scripts.length; ++i) {\n    const { url, script } = scripts[i];","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/denoland/deno/blob/a961cdec3b1948844414ebeecc697dcad76df2d1/runtime/js/99_main.js#L382-L418","documentation":"importScripts parses each argument with `new URL(scriptUrl, baseUrl)` against the worker's location; a parse failure throws DOMException(`Failed to parse URL: ${scriptUrl}`, 'SyntaxError') in runtime/js/99_main.js. The check is purely syntactic — it validates that the argument is an absolute URL or resolvable relative URL per the WHATWG parser; reachability is only tested later by op_worker_sync_fetch.","triggerScenarios":"`importScripts('not a url ::')`; strings with unencoded spaces or query fragments; Windows paths like 'C:\\lib.js'; a relative URL when the worker has no base href (baseUrl null).","commonSituations":"Building script URLs by string concatenation without encodeURIComponent; passing file paths from configuration; porting scripts that relied on eval-style path resolution.","solutions":["Pass a well-formed absolute or resolvable-relative URL, percent-encoded where needed","Pre-validate with `URL.canParse(scriptUrl, baseUrl)` (or a try/catch around new URL) before calling importScripts","Build URLs programmatically with `new URL(path, base).href` and pass the result"],"exampleFix":"// before\nimportScripts(base + '?file=' + fileName + '&mode=' + mode);\n\n// after\nimportScripts(\n  new URL(\n    `?file=${encodeURIComponent(fileName)}&mode=${encodeURIComponent(mode)}`,\n    base,\n  ).href,\n);","handlingStrategy":"validation","validationCode":"const base = typeof location !== 'undefined' ? location.href : undefined;\nif (!URL.canParse(scriptUrl, base)) {\n  throw new Error(`importScripts argument is not a parseable URL: ${scriptUrl}`);\n}\nimportScripts(scriptUrl);","typeGuard":null,"tryCatchPattern":"try {\n  importScripts(u);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'SyntaxError') {\n    // fix the URL string (encode components) and retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["Percent-encode dynamic query components with encodeURIComponent","Build URLs with new URL(...) instead of string concatenation","Pre-check user/config-derived URLs with URL.canParse before passing them to importScripts"],"tags":["url","importscripts","syntaxerror","workers","validation"],"backgroundTag":"invalid-url","analyzedSha":"a961cdec3b1948844414ebeecc697dcad76df2d1","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}