neon-bindings/neon · error · TypeError

expected JsBuffer

Error message

expected JsBuffer

What it means

This is the extraction error produced when `JsValue` cannot be downcast to `JsArrayBuffer` while extracting an `ArrayBuffer` argument in a Neon exported function. The `TryFromJs` impl for `ArrayBuffer<B>` returns `Self::Error` (whose message is "expected JsBuffer") when the incoming JS value is not an ArrayBuffer (detached views, plain objects, typed arrays, strings, etc. all fail).

Solutions

  1. Convert the JS value to a real ArrayBuffer before calling: `value.buffer` for typed arrays or `Uint8Array.from(buffer)` for Buffers
  2. Change the Rust signature to accept `JsBuffer`/`Buffer` if callers pass Node Buffers
  3. Accept a broader type in Rust (`&[u8]` via `JsTypedArray`) or handle the error in JS and retry with a converted value

Example fix

// before (JS)
const { readChunk } = require('./native');
readChunk(Buffer.from('data'));
// after (JS)
const buf = Buffer.from('data');
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
readChunk(ab);
Defensive patterns

Strategy: type-guard

Validate before calling

function isArrayBuffer(x) { return x instanceof ArrayBuffer; }

Type guard

function isRealArrayBuffer(v) { return v !== null && v !== undefined && v instanceof ArrayBuffer; }

Try / catch

try { native.readChunk(arg); } catch (e) { if (String(e).includes('expected JsBuffer')) { native.readChunk(toArrayBuffer(arg)); } else { throw e; } }

Prevention

When it happens

Trigger: Calling an exported Neon function whose parameter extracts as `ArrayBuffer` with an argument that is not a JS ArrayBuffer: a Buffer, Uint8Array, plain object, null, undefined, or a string.

Common situations: Passing a Node Buffer or Uint8Array from JS where the Rust signature expects `ArrayBuffer`; sending `null`/`undefined`; a caller on the JS side changed the value's type after a refactor.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13). Data as JSON: /api/errors/7f7da56d1ec00748. Report an issue: GitHub.

Appendix: source

Thrown at crates/neon/src/types_impl/extract/buffer.rs:29

    },
};

/// Wrapper for converting between bytes and [`JsArrayBuffer`](JsArrayBuffer)
pub struct ArrayBuffer<B>(pub B);

impl<'cx, B> TryFromJs<'cx> for ArrayBuffer<B>
where
    for<'b> B: From<&'b [u8]>,
{
    type Error = TypeExpected<JsBuffer>;

    fn try_from_js(
        cx: &mut Cx<'cx>,
        v: Handle<'cx, JsValue>,
    ) -> NeonResult<Result<Self, Self::Error>> {
        let v = match v.downcast::<JsArrayBuffer, _>(cx) {
            Ok(v) => v,
            Err(_) => return Ok(Err(Self::Error::new())),
        };

        Ok(Ok(ArrayBuffer(B::from(v.as_slice(cx)))))
    }
}

impl<'cx, B> TryIntoJs<'cx> for ArrayBuffer<B>
where
    B: AsRef<[u8]>,
{
    type Value = JsArrayBuffer;

    fn try_into_js(self, cx: &mut Cx<'cx>) -> JsResult<'cx, Self::Value> {
        JsArrayBuffer::from_slice(cx, self.0.as_ref())
    }
}

impl<B> private::Sealed for ArrayBuffer<B> {}

View on GitHub (pinned to 38960e4381)