
    // CPD-OFF — generated FFI boilerplate, identical across all opaque types by design.
    private static void checkLastFfiError() throws {{ exception_class }} {
        try {
            int code = (int) NativeLib.{{ prefix_upper }}_LAST_ERROR_CODE.invoke();
            if (code == 0) { return; }
            MemorySegment ctxPtr = (MemorySegment) NativeLib.{{ prefix_upper }}_LAST_ERROR_CONTEXT.invoke();
            String msg = ctxPtr.equals(MemorySegment.NULL) ? "unknown" : ctxPtr.reinterpret(Long.MAX_VALUE).getString(0);
            throw new {{ exception_class }}(code, msg);
        } catch ({{ exception_class }} e) {
            throw e;
        } catch (Throwable e) {
            throw new {{ exception_class }}("failed to read last error", e);
        }
    }
{%- if needs_read_bytes_result %}

    private byte[] readBytesResult(int rc, MemorySegment outPtrHolder, MemorySegment outLenHolder, MemorySegment outCapHolder) throws {{ exception_class }} {
        if (rc != 0) {
            checkLastFfiError();
            return null;
        }
        var outPtr = outPtrHolder.get(ValueLayout.ADDRESS, 0);
        long outLen = outLenHolder.get(ValueLayout.JAVA_LONG, 0);
        long outCap = outCapHolder.get(ValueLayout.JAVA_LONG, 0);
        if (outPtr.equals(MemorySegment.NULL)) {
            checkLastFfiError();
            return null;
        }
        byte[] result = outPtr.reinterpret(outLen).toArray(ValueLayout.JAVA_BYTE);
        try {
            {{ free_bytes }}.invoke(outPtr, outLen, outCap);
        } catch (Throwable e) {
            throw new {{ exception_class }}("failed to free FFI-allocated bytes", e);
        }
        return result;
    }
{%- endif %}
{%- if needs_stream_mapper %}

    private static final ObjectMapper STREAM_MAPPER = createStreamMapper();

    private static ObjectMapper createStreamMapper() {
        return new ObjectMapper()
            .registerModule(new com.fasterxml.jackson.datatype.jdk8.Jdk8Module())
            .findAndRegisterModules()
            .setPropertyNamingStrategy(com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE)
            .setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
            .configure(com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
    }
{%- endif %}
    // CPD-ON
