let cached_name = self.cached_name.clone();
let cached_name_inner = cached_name.clone();
let send_r_obj = SendRobj(self.inner.clone());

{% for param in params_to_clone %}
{{ param }}
{% endfor %}

tokio::task::spawn_blocking(move || {
    // Call `into_inner()` on the whole `SendRobj` value so the closure captures the whole
    // `SendRobj` (which is `Send`) rather than just the inner `Robj` field (which is `!Send`)
    // under 2021+ disjoint-capture rules. The method takes `self` by value, forcing the
    // closure to move the whole wrapper.
    let r_obj = send_r_obj.into_inner();
    let maybe_fn = r_obj.dollar("{{ method_name }}");
    let fn_robj = match maybe_fn {
        Ok(v) if !v.is_null() && !v.is_na() => v,
        _ => return Err({{ missing_method_error }}),
    };

{% if empty_args %}
    let result = fn_robj.call(extendr_api::Pairlist::new());
{% else %}
    let args = extendr_api::Pairlist::from_pairs(&[{{ args_pairs }}]);
    let result = fn_robj.call(args);
{% endif %}

    // R callbacks return JSON-encoded strings (e.g. via `jsonlite::toJSON`). Parse the string
    // payload back into the trait return type. Note: `extendr_api::Robj` does not implement
    // `serde::Serialize`, so we cannot serialize it directly — the caller must serialize on
    // the R side.
    match result {
        Err(_) => Err({{ failed_method_error }}),
        Ok(val) => {
{%- if native_return_binding %}
            // Native-object return: unwrap the host's native `ExternalPtr` and convert via
            // `From<{{ native_return_binding }}>` (mirrors the options decoder); fall through to the
            // JSON-string path when the host returned a string instead.
            if let Ok(ext) = extendr_api::prelude::ExternalPtr::<{{ native_return_binding }}>::try_from(&val) {
                return Ok((*ext).clone().into());
            }
{%- endif %}
            let payload = val.as_str().unwrap_or("");
            serde_json::from_str::<{{ return_type }}>(payload).map_err(|_| {{ deserialization_error }})
        }
    }
})
.await
.map_err(|e| {{ spawn_blocking_error }})?
