windmill-labs/windmill · error

fwrite is not supported

Error message

fwrite is not supported

What it means

The WASM Ruby parser stubs libc `fwrite` with an unconditional panic because writing to C stdio streams is impossible in the sandbox. Any Ruby VM or parsed code path that writes to a FILE* stream aborts with this message. It is a deliberate guard against unsupported libc surface.

Source

Thrown at backend/parsers/windmill-parser-ruby/src/wasm_libc.rs:266

#[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,
) -> usize {
    panic!("fwrite is not supported");
}

#[no_mangle]
pub unsafe extern "C" fn vsnprintf(
    _buf: *mut c_char,
    _size: usize,
    _format: *const c_char,
    _args: ...
) -> c_int {
    panic!("vsnprintf is not supported");
}

#[no_mangle]
pub extern "C" fn clock_gettime(ptr: usize, new_size: usize) {
    panic!("clock_gettime is not supported");
}

// int snprintf( char* restrict buffer, size_t bufsz, const char* restrict format, ... );

View on GitHub (pinned to e474e8803c)

Solutions

  1. Remove stream writes from the Ruby code under parse; return values instead of writing them out
  2. Use in-memory constructs (strings, arrays) rather than IO objects
  3. Implement a WASI-backed fwrite shim if output streaming is a real requirement

Example fix

// before
File.open('out.txt', 'w') { |f| f.write(data) }
// after
return data # let the host handle persistence
Defensive patterns

Strategy: validation

Validate before calling

fn writes_to_streams(ruby_src: &str) -> bool {
    ["File.open", "IO.write", "f.write", ".puts", ".print"]
        .iter().any(|p| ruby_src.contains(p))
}

Try / catch

match std::panic::catch_unwind(|| parse_ruby(source)) {
    Ok(res) => res,
    Err(e) => { log::warn!("ruby parser: stream write attempted in sandbox"); Err(format_parser_error(e)) }
}

Prevention

When it happens

Trigger: Calling the exported `fwrite` symbol, or Ruby code whose execution path performs buffered stream writes (puts/print to a stdio FILE*, gem internals using fwrite).

Common situations: Ruby script prints to a file handle or a gem writes via stdio during parsing; no real stream exists in wasm so the stub fires.

Related errors


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