windmill-labs/windmill · error

fputs is not supported

Error message

fputs is not supported

What it means

windmill-parser-r compiles R analysis to WASM and only stubs the libc symbols the binary links against; unsupported ones are `no_mangle` shims that panic unconditionally. `fputs` (write a string to a FILE* stream) has no WASM backing here, so any call aborts with this panic. It signals the parsed/embedded R code path tried to do buffered stdio output the sandbox does not implement.

Source

Thrown at backend/parsers/windmill-parser-r/src/wasm_libc.rs:241

}

/* --------------------------------- ctype.h -------------------------------- */

#[no_mangle]
pub unsafe extern "C" fn isprint(c: c_int) -> bool {
    c >= 32 && c <= 126
}

/* --------------------------------- stdio.h -------------------------------- */

#[no_mangle]
pub unsafe extern "C" fn fprintf(_file: *mut c_void, _format: *const c_void, _args: ...) -> c_int {
    panic!("fprintf is not supported");
}

#[no_mangle]
pub unsafe extern "C" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int {
    panic!("fputs is not supported");
}

#[no_mangle]
pub unsafe extern "C" fn fputc(_c: c_int, _file: *mut c_void) -> c_int {
    panic!("fputc is not supported");
}

#[no_mangle]
pub unsafe extern "C" fn fdopen(_fd: c_int, _mode: *const c_void) -> *mut c_void {
    panic!("fdopen is not supported");
}

#[no_mangle]
pub unsafe extern "C" fn fclose(_file: *mut c_void) -> c_int {
    panic!("fclose is not supported");
}

#[no_mangle]

View on GitHub (pinned to e474e8803c)

Solutions

  1. Rewrite the R code to use the parser's supported output mechanism instead of fputs (e.g. functions whose output the host captures via supported primitives)
  2. Check the wasm_libc.rs stub table and add a real implementation that forwards the bytes to the host if string output is required
  3. Remove or replace the dependent library code path that emits via FILE* streams
  4. Report/track as unsupported-feature if the R source legitimately needs stdio

Example fix

// before (stub)
#[no_mangle]
pub unsafe extern "C" fn fputs(_s: *const c_void, _file: *mut c_void) -> c_int {
    panic!("fputs is not supported");
}
// after (forward to host)
#[no_mangle]
pub unsafe extern "C" fn fputs(s: *const c_char, _file: *mut c_void) -> c_int {
    let cstr = CStr::from_ptr(s).to_string_lossy();
    print!("{}", cstr);
    cstr.len() as c_int
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Static check before running the R payload through the parser
const rSource = "...";
if (/\b(fputs|fputc|fwrite|fdopen|fclose)\s*\(/.test(rSource) || /\.C\(|\.Call\(/.test(rSource)) {
  throw new Error("R source uses stdio/file APIs unsupported by the R parser WASM sandbox");
}

Type guard

// Rust: never assume a libc stub is functional; treat FILE*-taking externs as unsupported
fn stream_io_supported(symbol: &str) -> bool {
    !matches!(symbol, "fputs" | "fputc" | "fwrite" | "fdopen" | "fclose" | "fprintf" | "fopen")
}

Try / catch

match std::panic::catch_unwind(|| parser_run_r(source)) {
    Ok(result) => result,
    Err(p) => {
        let msg = p.downcast_ref::<String>().map(String::as_str)
            .or_else(|| p.downcast_ref::<&str>().copied())
            .unwrap_or("unknown panic");
        if msg.contains("not supported") {
            // fall back: reject or rewrite the R source to avoid stdio calls
        } else { std::panic::resume_unwind(p); }
    }
}

Prevention

When it happens

Trigger: Calling the exported `fputs` shim in backend/parsers/windmill-parser-r/src/wasm_libc.rs, typically reached when R/compiled code writes to stdout/stderr or a file via fputs.

Common situations: R code in a script does `cat()`/`writeLines()` compiled down to fputs, or a C-shim/static library linked into the WASM calls fputs; running the parser outside the intended output channel where stdout is not wired up.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/73e490dcb4060c1f. Report an issue: GitHub.