{"record":{"id":"4d8f1b3a1c59374a","repo":"laurent22/joplin","slug":"filenotfound","errorCode":"fileNotFound","errorMessage":"File not found: ${options.path}","messagePattern":"File not found: (.+?)","errorType":"exception","errorClass":"JoplinError","httpStatus":null,"severity":"error","filePath":"packages/lib/file-api.ts","lineNumber":405,"sourceCode":"\n\t\tif (!output) return output;\n\t\toutput.path = path;\n\t\treturn output;\n\t}\n\n\t// Returns UTF-8 encoded string by default, or a Response if `options.target = 'file'`\n\tpublic get(path: string, options: GetOptions = null) {\n\t\tif (!options) options = {};\n\t\tif (!options.encoding) options.encoding = 'utf8';\n\t\tlogger.debug(`get ${this.fullPath(path)}`);\n\t\treturn tryAndRepeat(() => this.driver_.get(this.fullPath(path), options), this.requestRepeatCount());\n\t}\n\n\tpublic async put(path: string, content: string | Buffer | null, options: PutOptions = null) {\n\t\tlogger.debug(`put ${this.fullPath(path)}`, options);\n\n\t\tif (options && options.source === 'file') {\n\t\t\tif (!(await this.fsDriver().exists(options.path))) throw new JoplinError(`File not found: ${options.path}`, 'fileNotFound');\n\t\t}\n\n\t\treturn tryAndRepeat(() => this.driver_.put(this.fullPath(path), content, options), this.requestRepeatCount());\n\t}\n\n\tpublic async multiPut(items: MultiPutItem[], options: { source?: string } = null) {\n\t\tif (!this.driver().supportsMultiPut) throw new Error('Multi PUT not supported');\n\t\treturn tryAndRepeat(() => this.driver_.multiPut(items, options), this.requestRepeatCount());\n\t}\n\n\tpublic async multiDelete(paths: string[]) {\n\t\tif (!this.supportsMultiDelete) throw new Error('Multi DELETE not supported');\n\t\treturn tryAndRepeat(() => this.driver_.multiDelete(paths), this.requestRepeatCount());\n\t}\n\n\tpublic delete(path: string) {\n\t\tlogger.debug(`delete ${this.fullPath(path)}`);\n\t\treturn tryAndRepeat(() => this.driver_.delete(this.fullPath(path)), this.requestRepeatCount());","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/file-api.ts#L387-L423","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait fileApi.put(remotePath, null, { source: 'file', path: localPath });\n// after - guard before the call\nif (!(await shim.fsDriver().exists(localPath))) {\n  throw new Error(`Source file vanished before upload: ${localPath}`);\n}\nawait fileApi.put(remotePath, null, { source: 'file', path: localPath });","handlingStrategy":"validation","validationCode":"if (options?.source === 'file') {\n  if (!(await shim.fsDriver().exists(options.path))) {\n    throw new Error(`Cannot upload: source file missing: ${options.path}`);\n  }\n}","typeGuard":"function isFileSourceUpload(options: any): options is { source: 'file'; path: string } {\n  return options && options.source === 'file' && typeof options.path === 'string';\n}","tryCatchPattern":"try {\n  await fileApi.put(remotePath, null, { source: 'file', path: localPath });\n} catch (e) {\n  if (e.code === 'fileNotFound' && /File not found/.test(e.message)) {\n    // source vanished - regenerate, restore, or skip\n    logger.warn('Upload skipped, source missing:', localPath);\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["sync","upload","filesystem","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}