{"record":{"id":"bb989be6509e4323","repo":"windmill-labs/windmill","slug":"recording-upload-failed-error-message","errorCode":null,"errorMessage":"Recording upload failed: ${error.message}","messagePattern":"Recording upload failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/src/commands/app/dev.ts","lineNumber":793,"sourceCode":"        if (error?.code !== \"EEXIST\") throw error;\n      }\n    }\n    throw new Error(\"Could not find a free recording file name\");\n  }\n\n  function saveRecording(req: http.IncomingMessage, res: http.ServerResponse) {\n    if (!isOwnOrigin(req.headers.origin, req.headers.host)) {\n      sendJson(res, 403, { error: \"Cross-origin recording upload refused\" });\n      return;\n    }\n    const chunks: Buffer[] = [];\n    let size = 0;\n    let refused = false;\n    // An upload cut short (by the refusal below, or by the browser) raises\n    // 'error' on the request, which unhandled takes the dev server down.\n    req.on(\"error\", (error: Error) => {\n      if (!refused) {\n        log.warn(colors.yellow(`Recording upload failed: ${error.message}`));\n      }\n    });\n    req.on(\"data\", (chunk: Buffer) => {\n      if (refused) return;\n      size += chunk.length;\n      if (size > MAX_RECORDING_BYTES) {\n        refused = true;\n        // Torn down only once the 413 is on the wire: destroying the socket\n        // first loses the response the browser is waiting to read.\n        res.on(\"finish\", () => req.destroy());\n        sendJson(res, 413, {\n          error: `Recording exceeds ${MAX_RECORDING_BYTES} bytes`,\n        });\n        return;\n      }\n      chunks.push(chunk);\n    });\n    req.on(\"end\", async () => {","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/app/dev.ts#L775-L811","documentation":"The app dev recorder's upload endpoint listens for request errors; an aborted/cut-short upload (browser cancel, client disconnect, or server-side refusal after MAX_RECORDING_BYTES) triggers this warning instead of crashing the dev server.","triggerScenarios":"An HTTP request error on the recording upload: client closed the connection mid-upload, network drop, or the server refused the upload after the size limit was exceeded.","commonSituations":"Closing the browser tab while a recording uploads; recording larger than MAX_RECORDING_BYTES; flaky local network/proxy killing the connection.","solutions":["Retry the recording, keeping the session open until upload completes","Reduce recording size/length so it stays under MAX_RECORDING_BYTES","Verify no proxy/antivirus is interrupting localhost connections","This is a logged warning, not a crash — check the dev server log for context"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"req.on('error', (error: Error) => {\n  if (!refused) log.warn(colors.yellow(`Recording upload failed: ${error.message}`));\n});","typeGuard":"function isNodeReqError(e: unknown): e is NodeJS.ErrnoException {\n  return e instanceof Error && ('code' in e || 'errno' in e);\n}","tryCatchPattern":"req.on('error', (error: Error) => {\n  if (!refused) log.warn(colors.yellow(`Recording upload failed: ${error.message}`));\n});","preventionTips":["Keep the browser tab open until the recording upload finishes","Keep recordings under MAX_RECORDING_BYTES","Attach an 'error' handler on upload requests so the dev server never crashes","Check proxies/firewalls that reset localhost connections"],"tags":["http","upload","recording","cli"],"backgroundTag":"upload-aborted","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}