laurent22/joplin · error · Error

uploadBlob: ${method} ${url}: ${error.toString()}

Error message

uploadBlob: ${method} ${url}: ${error.toString()}

What it means

Wraps any exception from the RNFetchBlob-backed mobile upload path (shim.uploadBlob). The catch on line 154 flattens the original error into 'uploadBlob: <method> <url>: <original>'. Like fetchBlob, the structured RNFetchBlob error and HTTP status are lost in the re-thrown generic Error.

Source

Thrown at packages/app-mobile/utils/shim-init-react/index.ts:155

		const headers = options.headers ? options.headers : {};
		const method = options.method ? options.method : 'POST';

		try {
			const response = await RNFetchBlob.config({
				trusty: options.ignoreTlsErrors,
			}).fetch(method, url, headers, RNFetchBlob.wrap(options.path));

			// Returns an object that's roughly compatible with a standard Response object
			return {
				ok: response.respInfo.status < 400,
				data: response.data,
				text: response.text,
				json: response.json,
				status: response.respInfo.status,
				headers: response.respInfo.headers,
			};
		} catch (error) {
			throw new Error(`uploadBlob: ${method} ${url}: ${error.toString()}`);
		}
	};

	shim.readLocalFileBase64 = async function(path) {
		return RNFetchBlob.fs.readFile(path, 'base64');
	};

	shim.openUrl = url => {
		return Linking.openURL(url);
	};

	shim.mobilePlatform = () => {
		return Platform.OS as MobilePlatform;
	};

	shim.isAppleSilicon = () => {
		return false;
	};

View on GitHub (pinned to 2654b33620)

Solutions

  1. Confirm options.path exists and is a native filesystem path (not a file:// URI) before calling uploadBlob.
  2. Check auth token validity and refresh if the server returns 401/403 (visible via shim.debugFetch on the same URL).
  3. For large files, verify the sync target's max upload size and increase it or chunk the resource.
  4. Pass ignoreTlsErrors only in dev for self-signed endpoints; for production install the correct root CA.
  5. Upgrade rn-fetch-blob/react-native-blob-util to match the React Native version.

Example fix

// before
await shim.uploadBlob(url, { path: resourcePath, method: 'POST' });
// after — guard the source file and capture status
if (!(await shim.fsDriver().exists(resourcePath))) throw new Error('Missing upload source');
const resp = await shim.uploadBlob(url, { path: resourcePath, method: 'POST', headers: authHeaders });
if (!resp.ok) throw new Error(`Upload rejected: ${resp.status}`);
Defensive patterns

Strategy: retry

Validate before calling

if (!(await shim.fsDriver().exists(options.path))) throw new Error('uploadBlob: source missing');
if (!options.headers?.Authorization) throw new Error('uploadBlob: auth header missing');
await shim.uploadBlob(url, options);

Type guard

function isUploadOptions(o) { return !!o && typeof o.path === 'string' && (!!o.method || true); }

Try / catch

try {
  const resp = await shim.uploadBlob(url, options);
  if (!resp.ok) throw new Error(`upload ${resp.status}`);
} catch (e) {
  if (/Network|timeout/i.test(e.message)) await backoffRetry(() => shim.uploadBlob(url, options));
  else throw e;
}

Prevention

When it happens

Trigger: Uploading a resource blob to the sync target while offline; options.path does not exist or is unreadable; the server rejects the multipart body (auth failure, 413 payload too large, wrong content-type); TLS failure on the upload endpoint; RNFetchBlob.wrap() fails on a non-native path.

Common situations: E2EE sync upload to Joplin Server/Nextcloud/OneDrive with an expired token; uploading a large attachment exceeding server body limit; the source file was moved/deleted after the path was computed; reverse proxy timing out on slow upstream.

Related errors


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