{"record":{"id":"1a7b50147c784db2","repo":"laurent22/joplin","slug":"not-a-valid-url-url","errorCode":null,"errorMessage":"Not a valid URL: ${url}","messagePattern":"Not a valid URL: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/utils/shim-init-react/shimInitShared.ts","lineNumber":34,"sourceCode":"\tshim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js');\n\n\tshim.stringByteLength = function(string) {\n\t\treturn Buffer.byteLength(string, 'utf-8');\n\t};\n\n\tshim.httpAgent = () => null;\n\n\tshim.fetch = async function(url, options = null) {\n\t\t// The native fetch() throws an uncatchable error that crashes the\n\t\t// app if calling it with an invalid URL such as '//.resource' or\n\t\t// \"http://ocloud. de\" so detect if the URL is valid beforehand and\n\t\t// throw a catchable error. Bug:\n\t\t// https://github.com/facebook/react-native/issues/7436\n\t\tlet validatedUrl = '';\n\t\ttry { // Check if the url is valid\n\t\t\tvalidatedUrl = new URL(url).href;\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\n\t\treturn shim.fetchWithRetry(() => {\n\t\t\t// If the request has a body and it's not a GET call, and it\n\t\t\t// doesn't have a Content-Type header we display a warning,\n\t\t\t// because it could trigger a \"Network request failed\" error.\n\t\t\t// https://github.com/facebook/react-native/issues/30176\n\t\t\tif (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) {\n\t\t\t\tconsole.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options);\n\t\t\t}\n\n\t\t\t// Among React Native `fetch()` many bugs, one of them is that\n\t\t\t// it will truncate strings when they contain binary data.\n\t\t\t// Browser fetch() or Node fetch() work fine but as always RN's\n\t\t\t// one doesn't. There's no obvious way to fix this so we'll\n\t\t\t// have to wait if it's eventually fixed upstream. See here for\n\t\t\t// more info:\n\t\t\t// https://github.com/laurent22/joplin/issues/3986#issuecomment-718019688","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/utils/shim-init-react/shimInitShared.ts#L16-L52","documentation":"Thrown by shim.fetch (the React Native fetch wrapper) when new URL(url) throws a TypeError, i.e. the URL string is not parseable. This pre-validation exists because React Native's native fetch crashes the app with an uncatchable error on malformed URLs (see facebook/react-native#7436). The wrapper converts that into a catchable Error so callers can handle it.","triggerScenarios":"Passing '//' or '//.resource'; a URL with an illegal space such as 'http://ocloud. de'; undefined/null/non-string coerced to a bad string; a relative URL with no base; a URL with embedded control characters; a sync target misconfigured in settings.","commonSituations":"A resource or sync URL loaded from user settings contains a typo or trailing space; an attachment URL from a server response is malformed; clipboard/imported data injected an invalid URL; a proxy URL env var is empty producing '//'.","solutions":["Sanitize and validate URLs at the source: new URL(candidate) before storing in settings or passing to shim.fetch.","Trim whitespace and strip control characters from user-entered URLs.","If a relative URL is intended, supply a base: new URL(relative, base).href.","Check the sync target configuration in Settings for stray characters."],"exampleFix":"// before\nawait shim.fetch(userInputUrl, { method: 'GET' });\n// after — validate first, give the user feedback\ntry {\n\tnew URL(userInputUrl);\n} catch {\n\tthrow new Error(`Please enter a valid URL (got: ${userInputUrl})`);\n}\nawait shim.fetch(userInputUrl.trim(), { method: 'GET' });","handlingStrategy":"validation","validationCode":"function assertValidUrl(candidate) {\n  if (typeof candidate !== 'string') throw new Error('URL must be a string');\n  const trimmed = candidate.trim();\n  try { new URL(trimmed); return trimmed; }\n  catch { throw new Error(`Not a valid URL: ${candidate}`); }\n}\nconst safe = assertValidUrl(url);\nawait shim.fetch(safe, options);","typeGuard":"function isValidUrl(s) {\n  if (typeof s !== 'string') return false;\n  try { new URL(s.trim()); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  await shim.fetch(url, options);\n} catch (e) {\n  if (/Not a valid URL/.test(e.message)) {\n    // prompt user to fix the URL in settings\n  } else throw e;\n}","preventionTips":["Validate URLs at the input boundary (settings form, import path) with new URL().","Trim whitespace and strip control characters from user-entered URLs.","Never pass undefined/null/relative URLs without a base."],"tags":["mobile","network","url-validation","react-native","fetch"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}