{"record":{"id":"ef20bed404c69cd6","repo":"laurent22/joplin","slug":"not-a-valid-url-url-shim-init-node","errorCode":null,"errorMessage":"Not a valid URL: ${url}","messagePattern":"Not a valid URL: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/lib/shim-init-node.ts","lineNumber":552,"sourceCode":"\t\t\tconst imageDataURI = require('image-data-uri');\n\t\t\tconst result = imageDataURI.decode(imageDataUrl);\n\t\t\tawait shim.fsDriver().writeFile(filePath, result.dataBuffer, 'buffer');\n\t\t}\n\t};\n\n\tconst nodeFetch = require('node-fetch');\n\n\t// Not used??\n\tshim.readLocalFileBase64 = path => {\n\t\tconst data = fs.readFileSync(path);\n\t\treturn new Buffer(data).toString('base64');\n\t};\n\n\tshim.fetch = async function(url, options = {}) {\n\t\ttry { // Check if the url is valid\n\t\t\tnew URL(url);\n\t\t} catch (error) { // If the url is not valid, a TypeError will be thrown\n\t\t\tthrow new Error(`Not a valid URL: ${url}`);\n\t\t}\n\t\tconst resolvedProxyUrl = resolveProxyUrl(proxySettings.proxyUrl);\n\t\tif (resolvedProxyUrl && proxySettings.proxyEnabled) {\n\t\t\toptions.agent = shim.proxyAgent(url, resolvedProxyUrl);\n\t\t} else {\n\t\t\t// node-fetch calls this for every request, including each redirect hop, so the agent\n\t\t\t// always matches the protocol actually being used.\n\t\t\tconst agents = shim.httpAgents();\n\t\t\toptions.agent = (parsedUrl: URL) => parsedUrl.protocol === 'https:' ? agents.https : agents.http;\n\t\t}\n\t\treturn shim.fetchWithRetry(() => {\n\t\t\treturn nodeFetch(url, options);\n\t\t}, options);\n\t};\n\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- url is passed as a string but reassigned to a parsed UrlWithStringQuery by urlParse below\n\tshim.fetchBlob = async function(url: any, options: FetchBlobOptions) {\n\t\tif (!options || !options.path) throw new Error('fetchBlob: target file path is missing');","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/laurent22/joplin/blob/981a03c5c9e88130bccff4db47c411d35ec7ae2c/packages/lib/shim-init-node.ts#L534-L570","documentation":"shim.fetch wraps network requests and pre-validates the URL with `new URL(url)`. If URL parsing throws a TypeError, the fetch is aborted with a clearer 'Not a valid URL' message rather than an opaque parser error.","triggerScenarios":"Calling shim.fetch (or APIs built on it, like resource downloads or sync/upgrade checks) with a malformed, empty, or non-absolute URL string — e.g. missing protocol ('example.com/api'), whitespace, or undefined coerced to a string.","commonSituations":"Config files with a missing/typo'd server URL (e.g. Joplin Server URL without https://); relative URLs passed to fetch in Node/Electron context; empty string settings read before configuration completes.","solutions":["Fix the URL to include a valid scheme and host (e.g. prepend 'https://').","Validate with `new URL(url)` in a try-catch before calling fetch.","Check where the URL comes from (setting/env/arg) and fix the source value.","Log the raw input value to spot empty or truncated URLs."],"exampleFix":"// before\nawait shim.fetch(config.serverUrl);\n// after\nconst url = new URL(config.serverUrl || 'https://localhost:22300');\nawait shim.fetch(url.toString());","handlingStrategy":"validation","validationCode":"function isValidUrl(u: string): boolean { try { new URL(u); return true; } catch { return false; } }\nif (isValidUrl(url)) await shim.fetch(url);","typeGuard":"const isUrl = (u: unknown): u is string => typeof u === 'string' && u.length > 0 && (() => { try { new URL(u); return true; } catch { return false; } })();","tryCatchPattern":"try { await shim.fetch(url, opts); } catch (e) { if (String(e.message).startsWith('Not a valid URL:')) throw new Error(`Check server URL setting: ${e.message}`); else throw e; }","preventionTips":["Normalize user-provided URLs (trim, prepend scheme) before storing","Validate URL settings at save time, not fetch time","Never pass empty/undefined strings into fetch"],"tags":["network","url","validation","fetch"],"backgroundTag":"invalid-url-format","analyzedSha":"981a03c5c9e88130bccff4db47c411d35ec7ae2c","analyzedAt":"2026-09-17T14:49:40.960Z","contentChangedAt":"2026-09-17T14:49:40.960Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}