windmill-labs/windmill · error

fputc is not supported

Error message

fputc is not supported

What it means

`fputc` (write a single character to a FILE*) is one of the intentionally unimplemented libc shims in the R parser's WASM runtime: the stub exists only to satisfy linking and panics when entered. Hitting it means executing code that performs character-level stream output the sandbox does not support.

Source

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

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]
pub unsafe extern "C" fn fwrite(
    _ptr: *const c_void,
    _size: usize,
    _nmemb: usize,
    _stream: *mut c_void,

View on GitHub (pinned to e474e8803c)

Solutions

  1. Replace the calling code's char-by-char output with a supported mechanism (buffer the string and use a supported path)
  2. Implement the fputc shim to accumulate/forward output to the host instead of panicking
  3. Avoid linking the library or code path that depends on stdio character output
  4. Track as a known unsupported libc symbol if the code cannot be changed

Example fix

// before (stub)
#[no_mangle]
pub unsafe extern "C" fn fputc(_c: c_int, _file: *mut c_void) -> c_int {
    panic!("fputc is not supported");
}
// after (forward to host)
#[no_mangle]
pub unsafe extern "C" fn fputc(c: c_int, _file: *mut c_void) -> c_int {
    print!("{}", c as u8 as char);
    c
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Reject R/C sources that emit char-by-char via putc/fputc before execution
if (/\b(fputc|putc)\s*\(/.test(rSource)) {
  throw new Error("char-level stdio output (fputc) is unsupported in the R parser WASM");
}

Type guard

fn is_unsupported_stdio_symbol(sym: &str) -> bool {
    matches!(sym, "fputc" | "putc" | "fputs" | "fwrite" | "fclose" | "fdopen")
}

Try / catch

let outcome = std::panic::catch_unwind(AssertUnwindSafe(|| run_wasm_r(payload)));
if let Err(panic) = outcome {
    let msg = panic_msg(&panic);
    if msg.contains("fputc is not supported") {
        // buffer the output path instead: rewrite source to build a string and return it
    } else { std::panic::resume_unwind(panic); }
}

Prevention

When it happens

Trigger: Calling the exported `fputc` shim at backend/parsers/windmill-parser-r/src/wasm_libc.rs:246, reached when embedded/compiled code writes characters to stdout/stderr or a file stream.

Common situations: C code compiled into the WASM uses putc/fputc for output loops; a dependency formats output char-by-char; the R parser is run with code that prints incrementally instead of returning a value.

Related errors


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