can1357/oh-my-pi · error · Error

native task `{tag}` panicked: {message}

Error message

native task `{tag}` panicked: {message}

What it means

The Blocking napi Task runs work on libuv's thread pool; because that thread crosses an extern "C" FFI boundary, a Rust panic must not escape (it would force-abort the host). The task wraps work in catch_unwind and, if the closure panicked, converts the panic into a napi GenericFailure Error whose message names the task tag and the panic message. The JS promise rejects with this error instead of the process crashing.

Source

Thrown at crates/pi-natives/src/task.rs:197

		let tag = self.tag;
		// Guard the napi-rs async-work FFI boundary. `execute` is registered as
		// a plain `unsafe extern "C" fn` (napi 3.9.4 `src/async_work.rs:109`),
		// so an unwind escaping this frame would cross a non-`C-unwind` FFI
		// edge and force-abort the host under Rust's stabilized C-unwind rules
		// (RFC 2945, stable since 1.81). The crash handler scope tells the
		// global panic hook this panic is about to be caught and mapped to a
		// `GenericFailure`, so it downgrades the report to a disk-only crash
		// log — no stderr dump, no default-hook chaining.
		match catch_unwind(AssertUnwindSafe(move || {
			crate::crash_handler::blocking_task_panic_scope(move || work(cancel_token))
		})) {
			Ok(result) => result,
			Err(payload) => {
				// Extract the message BEFORE touching the payload's destructor:
				// disposal is the one remaining step that can panic again.
				let message = crate::crash_handler::panic_payload(&*payload);
				dispose_panic_payload(payload);
				Err(Error::new(
					Status::GenericFailure,
					format!("native task `{tag}` panicked: {message}"),
				))
			},
		}
	}

	fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> {
		Ok(output)
	}
}

/// Dispose of a caught panic payload without any possibility of a second
/// unwind escaping this frame.
///
/// A [`std::panic::panic_any`] payload is an arbitrary user type whose `Drop`
/// impl may itself panic. [`Blocking::compute`] runs inside napi's async-work
/// `extern "C"` frame, so a panic escaping the payload's destructor would

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded panic message after 'panicked: ' to identify the failing native operation and cause
  2. Check the input you passed (sizes, encodings, null/undefined fields) against the function's documented contract
  3. Retry once to rule out transient state, then report the input as a native-module bug with the panic message
  4. Update the native module — panics here are usually fixed upstream as bugs

Example fix

// before
const result = await native.someBlockingTask(buf);
// after
let result;
try {
  result = await native.someBlockingTask(buf);
} catch (err) {
  if (String(err?.message).startsWith('native task `someBlockingTask` panicked')) {
    result = null; // fall back to JS implementation
  } else throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (buf != null && typeof buf !== 'string' && !(buf instanceof Uint8Array)) throw new TypeError('expected string or Uint8Array');

Type guard

const isNativeInput = (v) => typeof v === 'string' || v instanceof Uint8Array;

Try / catch

try {
  return await native.someBlockingTask(input);
} catch (err) {
  const msg = String(err?.message ?? err);
  if (msg.startsWith('native task `someBlockingTask` panicked')) {
    log.error('native panic', { panic: msg.split('panicked: ')[1] });
    return jsFallback(input);
  } throw err;
}

Prevention

When it happens

Trigger: Any blocking native operation dispatched through the Blocking task whose closure panics — e.g. slice index out of bounds, unwrap on None/Err, assert failure, or an explicit panic inside the native implementation while processing your input.

Common situations: Feeding malformed/unexpected input to a native function (malformed buffer, invalid UTF-8 boundary); a native bug triggered by an edge case; builds with different overflow-check settings than CI.

Related errors


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