can1357/oh-my-pi · error

InvalidArg

InvalidArg

Error message

error.to_string()

What it means

pi-natives' `utf8` helper copies a JS string into a Rust &str via napi_get_value_string_utf8 using a thread-local scratch arena. After copying, it validates the bytes with str::from_utf8; if Node-API returned bytes that aren't valid UTF-8, it returns a Node-API Error with Status::InvalidArg carrying the UTF-8 validation message. This should be practically unreachable (Node-API guarantees UTF-8 output), so hitting it signals arena/pointer misuse or a corrupt napi buffer rather than bad JS input.

Source

Thrown at crates/pi-natives/src/js.rs:234

pub fn utf8(value: JsString<'_>) -> Result<Utf8> {
	let raw = value.value();
	ARENA.with(|arena| {
		let (start, avail) = arena.tail(1);
		if avail >= 2 {
			// SAFETY: `start..start + avail` is past every committed range.
			let ptr = unsafe { arena.base().add(start) };
			let mut written = 0;
			// SAFETY: `raw` is a JS string owned by the live callback; Node-API
			// writes at most `avail - 1` bytes plus a NUL into the free tail.
			let status = unsafe {
				sys::napi_get_value_string_utf8(raw.env, raw.value, ptr.cast(), avail, &mut written)
			};
			napi::check_status!(status, "Failed to read JavaScript string")?;
			if written < avail - 1 {
				// SAFETY: Node-API initialised `written` bytes at `ptr`.
				let bytes = unsafe { slice::from_raw_parts(ptr, written) };
				if let Err(error) = str::from_utf8(bytes) {
					return Err(Error::new(Status::InvalidArg, error.to_string()));
				}
				arena.commit(start, written);
				return Ok(Utf8(TextRepr::Scratch { ptr: NonNull::new(ptr).unwrap(), len: written }));
			}
		}

		let mut len = 0;
		// SAFETY: a null buffer asks Node-API for the byte length only.
		let status = unsafe {
			sys::napi_get_value_string_utf8(raw.env, raw.value, ptr::null_mut(), 0, &mut len)
		};
		napi::check_status!(status, "Failed to measure JavaScript string")?;
		let mut buf: Vec<u8> = Vec::with_capacity(len + 1);
		let mut written = 0;
		// SAFETY: `buf` holds the measured length plus the NUL slot.
		let status = unsafe {
			sys::napi_get_value_string_utf8(
				raw.env,

View on GitHub (pinned to 9690622007)

Solutions

  1. Rebuild the native addon from source (`cargo build -p pi-natives` / the package's install script) to eliminate binary/ABI mismatch, then reload the host process.
  2. Update to the latest pi-natives version — if this fires at all it is a library bug; check the changelog/issues for arena fixes and upgrade.
  3. Restart the Node/Bun process to reset the thread-local arena; if the error is transient it points at scratch-buffer corruption during a long-lived process.
  4. If it persists, reduce the input string to a minimal reproduction (length, code points) and file a bug — the InvalidArg is raised on library-internal bytes, not on user input.

Example fix

// before: stale prebuilt addon triggers invalid UTF-8 from the arena fast path
Error [InvalidArg]: invalid utf-8 sequence of 1 bytes from index 12

// after: rebuild the native module to match the current ABI
$ bun run build:natives   # or: cargo build --release -p pi-natives
$ node -e "require('./pi-natives')"  # restart the host process
Defensive patterns

Strategy: try-catch

Validate before calling

function isUsableNativeAddon(mod) {
  return typeof mod === 'object' && mod !== null && typeof mod.binding === 'object';
}

Try / catch

let result;
try {
  result = nativeCallWithString(str);
} catch (err) {
  if (err instanceof Error && err.code === 'InvalidArg') {
    // Library-internal UTF-8 validation failure: rebuild addon / restart process
    console.error('pi-natives returned InvalidArg; rebuild the native addon and retry');
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling the exposed native binding that takes a JS string argument when the fast-path arena copy yields invalid UTF-8 — e.g. after an arena overflow/miscommit bug, memory corruption, or a mismatched native binary that violates the napi_get_value_string_utf8 contract. Surfaces in JS as an Error with code 'InvalidArg'.

Common situations: A stale or ABI-mismatched pi-natives .node binary (native addon rebuilt against different Rust/Node versions) being loaded; running under a patched or exotic Node-API shim; reproducing after a partial arena commit corrupted the scratch buffer.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/f88098587d61306c. Report an issue: GitHub.