laurent22/joplin · error · Error

Not a valid URL: ${url}

Error message

Not a valid URL: ${url}

What it means

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.

Source

Thrown at packages/app-mobile/utils/shim-init-react/shimInitShared.ts:34

	shim.sjclModule = require('@joplin/lib/vendor/sjcl-rn.js');

	shim.stringByteLength = function(string) {
		return Buffer.byteLength(string, 'utf-8');
	};

	shim.httpAgent = () => null;

	shim.fetch = async function(url, options = null) {
		// The native fetch() throws an uncatchable error that crashes the
		// app if calling it with an invalid URL such as '//.resource' or
		// "http://ocloud. de" so detect if the URL is valid beforehand and
		// throw a catchable error. Bug:
		// https://github.com/facebook/react-native/issues/7436
		let validatedUrl = '';
		try { // Check if the url is valid
			validatedUrl = new URL(url).href;
		} catch (error) { // If the url is not valid, a TypeError will be thrown
			throw new Error(`Not a valid URL: ${url}`);
		}

		return shim.fetchWithRetry(() => {
			// If the request has a body and it's not a GET call, and it
			// doesn't have a Content-Type header we display a warning,
			// because it could trigger a "Network request failed" error.
			// https://github.com/facebook/react-native/issues/30176
			if (options?.body && options?.method && options.method !== 'GET' && !options?.headers?.['Content-Type']) {
				console.warn('Done a non-GET fetch call without a Content-Type header. It may make the request fail.', url, options);
			}

			// Among React Native `fetch()` many bugs, one of them is that
			// it will truncate strings when they contain binary data.
			// Browser fetch() or Node fetch() work fine but as always RN's
			// one doesn't. There's no obvious way to fix this so we'll
			// have to wait if it's eventually fixed upstream. See here for
			// more info:
			// https://github.com/laurent22/joplin/issues/3986#issuecomment-718019688

View on GitHub (pinned to 2654b33620)

Solutions

  1. Sanitize and validate URLs at the source: new URL(candidate) before storing in settings or passing to shim.fetch.
  2. Trim whitespace and strip control characters from user-entered URLs.
  3. If a relative URL is intended, supply a base: new URL(relative, base).href.
  4. Check the sync target configuration in Settings for stray characters.

Example fix

// before
await shim.fetch(userInputUrl, { method: 'GET' });
// after — validate first, give the user feedback
try {
	new URL(userInputUrl);
} catch {
	throw new Error(`Please enter a valid URL (got: ${userInputUrl})`);
}
await shim.fetch(userInputUrl.trim(), { method: 'GET' });
Defensive patterns

Strategy: validation

Validate before calling

function assertValidUrl(candidate) {
  if (typeof candidate !== 'string') throw new Error('URL must be a string');
  const trimmed = candidate.trim();
  try { new URL(trimmed); return trimmed; }
  catch { throw new Error(`Not a valid URL: ${candidate}`); }
}
const safe = assertValidUrl(url);
await shim.fetch(safe, options);

Type guard

function isValidUrl(s) {
  if (typeof s !== 'string') return false;
  try { new URL(s.trim()); return true; } catch { return false; }
}

Try / catch

try {
  await shim.fetch(url, options);
} catch (e) {
  if (/Not a valid URL/.test(e.message)) {
    // prompt user to fix the URL in settings
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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 '//'.

Related errors


AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12). Data as JSON: /api/errors/1a7b50147c784db2. Report an issue: GitHub.