laurent22/joplin · error · Error

tarExtract: Source file does not exist

Error message

tarExtract: Source file does not exist

What it means

Thrown by the mobile tarExtract helper when the source archive passed via options.file cannot be found at the resolved path. The function first computes filePath by checking whether the source is a URL (file:// or content://) and otherwise resolves it relative to cwd, then asserts existence with shim.fsDriver().exists(). Note: the URL-detection regex on line 16, /$[a-z]+:\/\//, is malformed ($ is an end-of-string anchor placed at the pattern start) so isSourceUrl is effectively always null, meaning even genuine file:///content:// URLs are passed through resolve(cwd, options.file) which mangles them.

Source

Thrown at packages/app-mobile/utils/fs-driver/tarExtract.ts:21

import shim from '@joplin/lib/shim';
import { chunkSize } from './constants';

export interface TarExtractOptions {
	cwd: string;
	file: string;
}

const tarExtract = async (options: TarExtractOptions) => {
	const cwd = options.cwd;

	// resolve doesn't correctly handle file:// or content:// URLs. Thus, we don't resolve relative
	// to cwd if the source is a URL.
	const isSourceUrl = options.file.match(/$[a-z]+:\/\//);
	const filePath = isSourceUrl ? options.file : resolve(cwd, options.file);

	const fsDriver = shim.fsDriver();
	if (!(await fsDriver.exists(filePath))) {
		throw new Error('tarExtract: Source file does not exist');
	}

	const extract = tarStreamExtract({ defaultEncoding: 'base64' });

	extract.on('entry', async (header, stream, next) => {
		const outPath = fsDriver.resolveRelativePathWithinDir(cwd, header.name);

		if (await fsDriver.exists(outPath)) {
			throw new Error(`Extracting ${outPath} would overwrite`);
		}

		// Allows moving to the next item after all data for this entry has been read
		// **and** this data has been processed.
		// See https://github.com/laurent22/joplin/issues/10285
		const streamEndPromise = new Promise<void>((resolve) => {
			stream.once('end', () => resolve());
		});

View on GitHub (pinned to 2654b33620)

Solutions

  1. Verify the source path exists before calling tarExtract: await shim.fsDriver().exists(options.file) and log the resolved absolute path.
  2. If passing a file:// or content:// URL, be aware the URL-detection regex is broken — pass a plain filesystem path instead, or patch tarExtract to use /^([a-z]+):\/\//.
  3. Confirm cwd is the directory you expect (e.g. RNFetchBlob.fs.dirs.DocumentDir) and that file is relative to it or already absolute.
  4. If restoring a backup, re-download or re-copy the tar to a known writable directory and pass that absolute path.

Example fix

// before
tarExtract({ cwd: dirs.DocumentDir, file: 'file:///cache/backup.tar' });
// after — use a plain path the driver can stat
const abs = `${dirs.DocumentDir}/backup.tar`;
await shim.fsDriver().copyFile(uriToPath(uri), abs);
tarExtract({ cwd: dirs.DocumentDir, file: abs });
Defensive patterns

Strategy: validation

Validate before calling

const fsDriver = shim.fsDriver();
const resolved = isUrl(options.file) ? options.file : resolve(options.cwd, options.file);
if (!(await fsDriver.exists(resolved))) {
  throw new Error(`tarExtract source missing: ${resolved}`);
}
await tarExtract(options);

// helper: the in-file regex is broken, so detect URLs correctly
function isUrl(s) { return /^([a-z]+):\/\//.test(s); }

Type guard

function isTarExtractOptions(v) {
  return v && typeof v.cwd === 'string' && typeof v.file === 'string' && v.file.length > 0;
}

Try / catch

try {
  await tarExtract(options);
} catch (e) {
  if (/Source file does not exist/.test(e.message)) {
    // re-resolve, re-download, or prompt user
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling tarExtract({ cwd, file }) where file is a path that does not exist on disk; passing a relative path that does not resolve under cwd; passing a file:// or content:// URI (because the broken regex never classifies it as a URL, resolve() corrupts the URI into a nonsense filesystem path); the file was deleted between the caller's check and the call; wrong cwd on Android where DocumentDir differs from expectation.

Common situations: Restoring a Joplin backup on mobile where the backup tar was moved or never fully downloaded; the backup path was constructed from a content:// URI that the FS driver cannot stat; iOS/Android sandbox path differences between the producer and consumer of the tar; a previous extraction partially failed leaving an inconsistent state.

Related errors


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