laurent22/joplin · error · JoplinError
fileNotFound
fileNotFound
Error message
File not found: ${options.path} What it means
Thrown by FileApi.put() when options.source === 'file' but the local file at options.path does not exist (checked via fsDriver().exists). The driver refuses to attempt a streaming upload of a missing file rather than letting the underlying driver fail with a cryptic stream error.
Source
Thrown at packages/lib/file-api.ts:405
if (!output) return output;
output.path = path;
return output;
}
// Returns UTF-8 encoded string by default, or a Response if `options.target = 'file'`
public get(path: string, options: GetOptions = null) {
if (!options) options = {};
if (!options.encoding) options.encoding = 'utf8';
logger.debug(`get ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.get(this.fullPath(path), options), this.requestRepeatCount());
}
public async put(path: string, content: string | Buffer | null, options: PutOptions = null) {
logger.debug(`put ${this.fullPath(path)}`, options);
if (options && options.source === 'file') {
if (!(await this.fsDriver().exists(options.path))) throw new JoplinError(`File not found: ${options.path}`, 'fileNotFound');
}
return tryAndRepeat(() => this.driver_.put(this.fullPath(path), content, options), this.requestRepeatCount());
}
public async multiPut(items: MultiPutItem[], options: { source?: string } = null) {
if (!this.driver().supportsMultiPut) throw new Error('Multi PUT not supported');
return tryAndRepeat(() => this.driver_.multiPut(items, options), this.requestRepeatCount());
}
public async multiDelete(paths: string[]) {
if (!this.supportsMultiDelete) throw new Error('Multi DELETE not supported');
return tryAndRepeat(() => this.driver_.multiDelete(paths), this.requestRepeatCount());
}
public delete(path: string) {
logger.debug(`delete ${this.fullPath(path)}`);
return tryAndRepeat(() => this.driver_.delete(this.fullPath(path)), this.requestRepeatCount());View on GitHub (pinned to 2654b33620)
Solutions
- Verify the file exists with fsDriver().exists(options.path) immediately before calling put() and handle the race if it may be deleted concurrently.
- Check the path is absolute and correct (no trailing slash, correct extension, right working directory).
- If the file is a temp resource being processed, ensure no concurrent cleanup (e.g. disable parallel garbage collection during sync).
- Regenerate or restore the missing source file before retrying.
Example fix
// before
await fileApi.put(remotePath, null, { source: 'file', path: localPath });
// after - guard before the call
if (!(await shim.fsDriver().exists(localPath))) {
throw new Error(`Source file vanished before upload: ${localPath}`);
}
await fileApi.put(remotePath, null, { source: 'file', path: localPath }); Defensive patterns
Strategy: validation
Validate before calling
if (options?.source === 'file') {
if (!(await shim.fsDriver().exists(options.path))) {
throw new Error(`Cannot upload: source file missing: ${options.path}`);
}
} Type guard
function isFileSourceUpload(options: any): options is { source: 'file'; path: string } {
return options && options.source === 'file' && typeof options.path === 'string';
} Try / catch
try {
await fileApi.put(remotePath, null, { source: 'file', path: localPath });
} catch (e) {
if (e.code === 'fileNotFound' && /File not found/.test(e.message)) {
// source vanished - regenerate, restore, or skip
logger.warn('Upload skipped, source missing:', localPath);
return;
}
throw e;
} Prevention
- Check fsDriver().exists(localPath) immediately before source=file uploads.
- Avoid concurrent cleanup of temp/resource files during sync.
- Pass absolute paths; verify the working directory.
- Restore or regenerate the source file if it was deleted.
When it happens
Trigger: Calling fileApi.put(path, null, { source: 'file', path: '/local/file.md' }) where /local/file.md has been moved, deleted, or the path is wrong before the PUT runs.
Common situations: Resource file was deleted between scheduling the upload and executing it; temp file already cleaned up by a concurrent process; wrong path passed (relative vs absolute, missing extension); external editor moved the file; antivirus quarantined the file on Windows.
Related errors
- s3UploadFileFrom: file does not exist
- Please specify the sync target path.
- Format "${format}" can only be exported to a file
- Only one output directory can be selected
- Item not found: ${itemId}
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/4d8f1b3a1c59374a.
Report an issue: GitHub.