windmill-labs/windmill · error

fputc is not supported

Error message

fputc is not supported

What it means

fputc is stubbed to panic in the Ruby parser WASM libc shim (wasm_libc.rs:246). Single-character output via C fputc cannot work in the sandbox (no real streams), so the shim only exists to satisfy linking of the compiled Ruby parser. A panic here means the interpreter tried to emit one character to a FILE*.

Source

Thrown at backend/parsers/windmill-parser-ruby/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. Implement fputc to buffer/forward characters to console_log in wasm_libc.rs and rebuild the WASM
  2. Disable or redirect the interpreter's output paths (verbose/warning configuration) so fputc is not called
  3. Upgrade windmill-parser-ruby to a build that no longer panics on fputc
  4. Modify the Ruby input that makes the parser emit output

Example fix

// before
pub unsafe extern "C" fn fputc(_c: c_int, _file: *mut c_void) -> c_int {
    panic!("fputc is not supported");
}
// after
pub unsafe extern "C" fn fputc(c: c_int, _file: *mut c_void) -> c_int {
    console_log!("{}", (c as u8) as char);
    c
}
Defensive patterns

Strategy: try-catch

Validate before calling

// No cheap pre-check exists; validate input shape to reduce diagnostic paths:
if (typeof rubySource !== 'string' || rubySource.length === 0) {
  throw new TypeError('rubySource must be a non-empty string');
}

Type guard

function isFputcPanic(e) {
  return e instanceof Error && e.message.includes('fputc is not supported');
}

Try / catch

try {
  const ast = rubyParser.parse(rubySource);
} catch (e) {
  if (isFputcPanic(e)) {
    console.warn('Ruby parser tried char-level output; skipping input');
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: The embedded Ruby parser calls C fputc — commonly from low-level character output routines in error printing or stream writing during parsing.

Common situations: Interpreter diagnostics built on character-at-a-time output; parser builds whose printf machinery falls back to fputc; WASM environments without stdio host imports.

Related errors


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