{"record":{"id":"1aa790f3f2af7c28","repo":"jdx/mise","slug":"cloned-request-builder-should-remain-cloneable","errorCode":null,"errorMessage":"cloned request builder should remain cloneable","messagePattern":"cloned request builder should remain cloneable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/vfox/src/lua_mod/http.rs","lineNumber":46,"sourceCode":"        () = cancelled => Err(mlua::Error::runtime(\"interrupted\")),\n    }\n}\n\nasync fn send_with_retry(builder: RequestBuilder) -> std::result::Result<Response, reqwest::Error> {\n    let url = builder\n        .try_clone()\n        .and_then(|b| b.build().ok())\n        .map(|r| r.url().to_string())\n        .unwrap_or_default();\n    let Some(template) = builder.try_clone() else {\n        return builder.send().await;\n    };\n\n    let attempts = http_retry_attempts().max(1);\n    for attempt in 0..attempts {\n        let response = template\n            .try_clone()\n            .expect(\"cloned request builder should remain cloneable\")\n            .send()\n            .await;\n\n        let transient_err: Option<String> = match response {\n            Ok(resp) if should_retry_status(resp.status()) && attempt + 1 < attempts => {\n                Some(format!(\"HTTP {}\", resp.status()))\n            }\n            Ok(resp) => return Ok(resp),\n            Err(err) if is_transient(&err) && attempt + 1 < attempts => Some(err.to_string()),\n            Err(err) => return Err(err),\n        };\n\n        if let Some(msg) = transient_err {\n            let delay = retry_delay(attempt);\n            log::warn!(\n                \"HTTP {} attempt {} failed (transient): {}; retrying in {:?}\",\n                url,\n                attempt + 1,","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/crates/vfox/src/lua_mod/http.rs#L28-L64","documentation":"The Lua http module retries requests by cloning a template reqwest::RequestBuilder each attempt. try_clone returns None if the builder holds a non-cloneable (streaming) body, so mise panics with this message. Plugin HTTP helpers are expected to send in-memory bodies only.","triggerScenarios":"A vfox plugin's Lua http:get/head call path (get_with_cancellation, head, try_* wrappers) -> send_with_retry when the request builder acquired a streaming body — usually from a plugin bug or a mise regression in how the request template is built.","commonSituations":"Third-party vfox plugins doing POSTs with streaming bodies; recent mise/plugin version mismatch; normally not reachable for plain GET/HEAD.","solutions":["Update the vfox plugin and mise to latest versions","If a specific plugin triggers it, report to that plugin and/or mise with the plugin name and a stack trace","As a workaround, use a plugin version/variant that uses simple in-memory requests"],"exampleFix":"// plugin code, before\nhttp:post(url, io_reader_body)\n// after\nhttp:post(url, \"in-memory-body-string\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// panic path; avoid by keeping plugin HTTP bodies in-memory\nassert!(template.try_clone().is_some(), \"plugin http requests must be cloneable\");","preventionTips":["Keep vfox plugin http bodies as strings/tables, not streaming handles","Pin tested plugin versions and update mise+plugins together","Report plugins that crash the Lua http layer to their maintainers"],"tags":["http","lua","panic","invariant","vfox"],"backgroundTag":"internal-invariant-violation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}