wasmerio/wasmer · error

Unable to cast to a {}

Error message

Unable to cast to a {}

What it means

js_array in lib/wasix/src/http/web_http_client.rs converts a JS array-like (e.g. the result of Array.from(headers)) into a fixed-size Rust array [T; N] by dyn_into-casting each element. If any element is not actually of type T (expected pairs of [name, value] arrays but found something else), the dyn_into fails and this error names the expected Rust type.

Source

Thrown at lib/wasix/src/http/web_http_client.rs:263

        header_map.insert(key, value);
    }

    Ok(header_map)
}

fn js_array<T, const N: usize>(value: &JsValue) -> Result<[T; N], anyhow::Error>
where
    T: JsCast,
{
    let array: &js_sys::Array = value.dyn_ref().context("Not an array")?;

    let mut items = Vec::new();

    for value in array.iter() {
        let item = value
            .dyn_into()
            .map_err(|_| anyhow::anyhow!("Unable to cast to a {}", std::any::type_name::<T>()))?;
        items.push(item);
    }

    <[T; N]>::try_from(items).map_err(|original| {
        anyhow::anyhow!(
            "Unable to turn a list of {} items into an array of {N} items",
            original.len()
        )
    })
}

pub async fn get_response_data(resp: &web_sys::Response) -> Result<Vec<u8>, anyhow::Error> {
    let buffer = JsFuture::from(resp.array_buffer().unwrap())
        .await
        .map_err(js_error)
        .with_context(|| "Could not retrieve response body".to_string())?;

    let buffer = js_sys::Uint8Array::new(&buffer);

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Check the inner JS environment: run in a standard browser with native Headers support, not a partial polyfill.
  2. Log/inspect the raw JS array before conversion to see the actual element shape.
  3. Ensure the response/request headers are plain string-to-string pairs; convert non-string values with String() on the JS side.
  4. Update the CLI/runtime — host binding mismatches between JS glue and Rust expectations are often fixed upstream.
  5. If you control the JS glue, normalize entries to [string, string] arrays before passing them across the boundary.

Example fix

// before (JS glue passes headers entries verbatim)
const entries = headers.entries();
fetchBytes(entries);
// after: normalize to [string, string] pairs
const entries = Array.from(headers.entries())
  .map(([k, v]) => [String(k), String(v)])
  .filter(([k, v]) => k != null && v != null);
fetchBytes(entries);
Defensive patterns

Strategy: type-guard

Validate before calling

// normalize the JS array shape before it reaches js_array
const entries = Array.from(headers.entries())
  .filter(e => Array.isArray(e) && e.length === 2
      && typeof e[0] === 'string' && typeof e[1] === 'string')
  .map(([k, v]) => [String(k), String(v)]);

Type guard

// Rust-side narrowing check before dyn_into
fn is_js_string_pair(v: &JsValue) -> bool {
    js_sys::Array::is_array(v)
        && js_sys::Array::from(v).length() == 2
        && js_sys::Array::from(v).get(0).is_string()
        && js_sys::Array::from(v).get(1).is_string()
}

Try / catch

match headers(client).await {
    Err(e) if e.to_string().contains("Unable to cast to a") => {
        eprintln!("{e}\nHint: JS headers entries are not [string, string] pairs — normalize on the JS side or update the runtime");
        std::process::exit(1);
    }
    other => other,
}

Prevention

When it happens

Trigger: The JS value iterated in `headers` contains elements that don't match the expected shape — e.g. Headers.entries() returned items that aren't length-2 string arrays, or the JS bridge returned objects instead of arrays.

Common situations: Running in a JS environment whose Headers implementation returns non-standard iteration results (polyfills, workers, Node vs browser differences); a header entry containing null/undefined values; mismatch between expected tuple arity and what the host returns.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/07f117cccab1cabe. Report an issue: GitHub.