{"record":{"id":"ea5a9b0ee93d0e98","repo":"RocketChat/Rocket.Chat","slug":"error-sending-file-to-apps","errorCode":null,"errorMessage":"Error sending file to apps","messagePattern":"Error sending file to apps","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/listeners.ts","lineNumber":262,"sourceCode":"\t\t\t\treturn this.defaultEvent(args);\n\t\t}\n\t}\n\n\tasync defaultEvent(args: HandleDefaultEvent): Promise<unknown> {\n\t\treturn this.orch.getManager().getListenerManager().executeListener(args.event, args.payload[0]);\n\t}\n\n\tasync uploadEvent(args: HandleFileUploadEvent): Promise<void> {\n\t\tconst [{ file, content }] = args.payload;\n\n\t\tconst tmpfile = path.join(this.orch.getManager().getTempFilePath(), crypto.randomUUID());\n\n\t\tif (typeof content === 'string') {\n\t\t\t// If content is a string, we assume it's a path and create a symlink to avoid file duplication\n\t\t\tawait fs.promises.symlink(content, tmpfile, 'file').catch((err) => {\n\t\t\t\tthis.orch.getRocketChatLogger().error({ msg: `AppListenerBridge: Could not create symlink at ${tmpfile}`, err });\n\n\t\t\t\tthrow new Error('Error sending file to apps', { cause: err });\n\t\t\t});\n\t\t} else {\n\t\t\t// Otherwise, we write the buffer content to a temporary file\n\t\t\tawait fs.promises.writeFile(tmpfile, content).catch((err) => {\n\t\t\t\tthis.orch.getRocketChatLogger().error({ msg: `AppListenerBridge: Could not write temporary file at ${tmpfile}`, err });\n\n\t\t\t\tthrow new Error('Error sending file to apps', { cause: err });\n\t\t\t});\n\t\t}\n\n\t\ttry {\n\t\t\tconst uploadDetails = {\n\t\t\t\tname: file.name || '',\n\t\t\t\tsize: file.size || 0,\n\t\t\t\ttype: file.type || '',\n\t\t\t\trid: file.rid || '',\n\t\t\t\tuserId: file.userId || '',\n\t\t\t\tvisitorToken: file.token,","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/app/apps/server/bridges/listeners.ts#L244-L280","documentation":"The Webdav upload stores pipe a read stream from the WebDAV server; any stream 'error' event logs 'An error ocurred when fetching the file' via SystemLogger and responds 503 with an empty body. 503 (not 404) marks the upstream store as the problem: connectivity, credentials, or the remote file — Rocket.Chat could not retrieve bytes from WebDAV.","triggerScenarios":"FileUpload_Webdav_Server_URL unreachable, TLS-invalid, or slow to the point of stream failure; FileUpload_Webdav_Username/Password wrong or rotated (settings are re-read via settings.watchByRegex); the remote file was deleted; connection dropped mid-transfer after partial data was written.","commonSituations":"WebDAV credential rotation not mirrored into settings; firewall/DNS changes breaking reachability; WebDAV server maintenance windows; partially uploaded files that open a stream then error.","solutions":["Verify the WebDAV server is reachable and credentials valid — re-save the FileUpload_Webdav_* settings to force reconfiguration","Correlate with the SystemLogger 'An error ocurred when fetching the file' entry at the same timestamp to confirm the store is the failure point","Re-upload or re-sync files missing on the WebDAV server","Reproduce with an external WebDAV client if the error persists; watch for partial content already written before the error"],"exampleFix":"// before: single attempt, surface as 500\nconst res = await fetch(fileUrl);\n// after: retry transient 503s from the store\nfor (let i = 0; i < 3; i++) {\n\tconst r = await fetch(fileUrl);\n\tif (r.status !== 503) return r;\n\tawait new Promise((ok) => setTimeout(ok, 2 ** i * 500));\n}\nthrow new Error('webdav store unavailable');","handlingStrategy":"retry","validationCode":"// preflight the upstream before serving\nawait fetch(settings.get('FileUpload_Webdav_Server_URL'), { method: 'OPTIONS' }).catch(() => { /* mark store unhealthy; fail fast with a clear message */ });","typeGuard":null,"tryCatchPattern":"Attach an 'error' handler on the read stream; on error, retry the whole getReadStream with exponential backoff (2-3 attempts) for transient 503s; abort retry on auth failures (401/403 from WebDAV) and surface the credential problem instead.","preventionTips":["Monitor WebDAV reachability and credential validity as a health check","Re-save FileUpload_Webdav_* settings after any credential rotation","Do not write partial bytes to res before the stream proves healthy — buffer the first chunk","Watch SystemLogger for 'An error ocurred when fetching the file' to catch store issues early"],"tags":["file-upload","webdav","http-503","storage","upstream"],"backgroundTag":"upstream-storage-unavailable","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}