{"record":{"id":"9cac0fa9bbeddf38","repo":"windmill-labs/windmill","slug":"result-over-budget","errorCode":null,"errorMessage":"result over budget","messagePattern":"result over budget","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-worker/src/worker.rs","lineNumber":1669,"sourceCode":"/// fail for any other reason, which is what makes that reading unambiguous.\npub(crate) fn to_raw_value_within<T: serde::Serialize>(\n    value: &T,\n    budget: usize,\n) -> Option<Box<serde_json::value::RawValue>> {\n    struct Budgeted {\n        buf: Vec<u8>,\n        left: usize,\n    }\n    impl std::io::Write for Budgeted {\n        fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {\n            self.write_all(bytes)?;\n            Ok(bytes.len())\n        }\n        // `Vec<u8>` overrides this too: the default implementation loops over\n        // `write`, and serde_json emits a great many small pieces per row.\n        fn write_all(&mut self, bytes: &[u8]) -> std::io::Result<()> {\n            if bytes.len() > self.left {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::WriteZero,\n                    \"result over budget\",\n                ));\n            }\n            self.left -= bytes.len();\n            self.buf.extend_from_slice(bytes);\n            Ok(())\n        }\n        fn flush(&mut self) -> std::io::Result<()> {\n            Ok(())\n        }\n    }\n\n    let mut writer = Budgeted { buf: Vec::new(), left: budget };\n    serde_json::to_writer(&mut writer, value).ok()?;\n    let json = String::from_utf8(writer.buf).ok()?;\n    // SAFETY: `to_writer` returned `Ok`, so `json` holds one complete, well-formed\n    // JSON value with no surrounding whitespace. Running out of budget is the only","sourceCodeStart":1651,"sourceCodeEnd":1687,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/worker.rs#L1651-L1687","documentation":"The result-streaming writer for job results enforces a byte budget: write_all into an in-memory Vec buffer checks the incoming chunk against the remaining allowance (self.left) and fails with io::ErrorKind::WriteZero and the message \"result over budget\" when exceeded. This caps how large a job result may be serialized into memory before it is sent to storage.","triggerScenarios":"A job returns a result whose serialized JSON exceeds the configured result size limit (e.g. MAX_SQL_RESULT_SIZE-style budget) while worker.rs streams the result into the buffer via write_all.","commonSituations":"Scripts returning huge arrays/dataframes, SQL jobs selecting unbounded rows, flow step outputs carrying full datasets instead of references, or an under-configured result limit for a legitimately large workload.","solutions":["Reduce the job's returned data: return only needed columns/rows, aggregate, or write large payloads to S3/object storage and return a reference.","Increase the worker/workspace result-size limit env (e.g. the result/streaming limit variable) if large results are expected and memory allows.","For SQL executors, add LIMIT/pagination to the query.","Check preceding flow steps for accidental pass-through of full outputs."],"exampleFix":"// before\nreturn all_rows; // serialized size exceeds budget\n// after\nlet limited = all_rows.into_iter().take(10_000).collect();\nreturn limited; // or upload to storage and return the URL","handlingStrategy":"validation","validationCode":"// Estimate the serialized size of a job result before returning it\nfn fits_result_budget<T: serde::Serialize>(value: &T, budget: usize) -> bool {\n    serde_json::to_vec(value).map(|b| b.len() <= budget).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match write_result(&mut writer, &value) {\n    Err(e) if e.kind() == std::io::ErrorKind::WriteZero => {\n        // result over budget: fall back to storing the payload in object storage\n        let url = upload_to_storage(&value)?;\n        write_result(&mut writer, &url)?;\n    }\n    other => other?,\n}","preventionTips":["Cap returned rows/columns with LIMIT and projections in SQL jobs","Offload large payloads to S3/workspace storage and return references","Configure the worker result-size limit to match expected workloads","In flows, avoid pass-through of full step outputs between steps"],"tags":["worker","result-size","memory-limit","streaming"],"backgroundTag":"result-size-limit-exceeded","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"}