Skip to content

bug(someip): RPC responses and events have empty payloads when opensomeip native extension is unavailable #628

@vtz

Description

@vtz

Summary

SomeIpDriverClient.rpc_call() returns SomeIpMessageResponse objects with payload='' (empty string) for every RPC call, and receive_event() always times out — even though the ECU is correctly sending payloads on the wire. The root cause is a silent degradation chain: when the opensomeip C++ extension fails to load, RpcClient.call() falls through to a stub code-path that returns a synthetic empty response instead of raising an error.

Environment

  • Platform: macOS (Darwin 25.4.0, Apple Silicon)
  • Jumpstarter mode: jmp shell --exporter-config export.yaml --direct
  • Driver: jumpstarter_driver_someip.driver.SomeIp (from PR feat: add SOME/IP driver wrapping opensomeip Python binding #391)
  • opensomeip: opensomeip==0.1.2 (Python binding for the C++ SOME/IP stack)
  • Target ECU: Nucleo H753ZI running Zephyr with SOME/IP services (door lock 0x1001, speed sensor 0x1003)

Exporter config

someip:
  type: jumpstarter_driver_someip.driver.SomeIp
  config:
    host: "192.168.100.1"
    port: 30490
    transport_mode: UDP
    remote_host: "192.168.100.10"
    remote_port: 30490

Reproduction steps

  1. Flash ECU with SOME/IP-capable firmware
  2. Run: jmp shell --exporter-config export.yaml --direct
  3. Execute a test script that makes RPC calls:
resp = someip.rpc_call(0x1001, 0x0003, b"", timeout=5.0)  # GetStatus
print(resp.return_code, resp.payload)
# Observed:  0, ''
# Expected:  0, '01'  (or '00')
  1. Subscribe to events:
someip.subscribe_eventgroup(0x0001)
event = someip.receive_event(timeout=10.0)
# Observed:  TimeoutError (DEADLINE_EXCEEDED)
# Expected:  SomeIpEventNotification with payload

Proof: ECU payloads are correct on the wire

Bypassing Jumpstarter entirely and running a native POSIX MPU application that uses the C++ opensomeip library against the same ECU on the same network, payloads arrive correctly:

[MPU-SOMEIP] rx event svc=0x1001 method=0x8001 payload=[0x00 0x01]
[MPU-SOMEIP] rx event svc=0x1003 method=0x8001 payload=[0x43 0x48 0x00 0x00]  ← 200.0 km/h (IEEE 754)
GetStatus response: payload=[0x01]  ← door locked

This confirms the ECU firmware and network are working. The issue is strictly in the Jumpstarter driver → opensomeip-python pipeline.

Root cause analysis

1. opensomeip C++ extension not loading (macOS libc++ ABI mismatch)

On macOS, opensomeip._opensomeip (the pybind11 C++ extension) commonly fails to load due to a libc++ ABI mismatch when installed from source with Homebrew LLVM. When this happens, opensomeip emits an ImportWarning and falls back to pure-Python stub classes. This warning is easy to miss because Python filters ImportWarning by default.

The _bridge.py module sets HAS_NATIVE = False and get_ext() returns None.

2. RpcClient.call() stub path returns a fake empty response

In opensomeip/rpc.py, when the C++ extension is unavailable, RpcClient.call() falls through to:

# Stub path — no actual network response is waited for
request = Message(
    message_id=method_id,
    request_id=RequestId(client_id=self._client_id, session_id=self._next_session()),
    message_type=MessageType.REQUEST,
    payload=payload,
)
self._transport.send(request)  # Also a no-op when cpp is None
return Message(
    message_id=method_id,
    request_id=request.request_id,
    message_type=MessageType.RESPONSE,
    return_code=ReturnCode.E_OK,
    # ← No payload! Defaults to b""
)

This path:

  • Sends the request via transport (which is also a no-op stub — no sockets opened)
  • Returns a synthetic Message with return_code=E_OK and empty payload
  • The caller has no way to distinguish this from a real response

Additionally, even when the C++ extension IS loaded, call_method_sync failures are silently swallowed:

if self._cpp is not None:
    try:
        result = self._cpp.call_method_sync(...)
        if int(result.result) == 0:
            return Message(...)  # Only returns here on success
    except Exception:
        pass  # ← Silently falls through to the stub path!

3. Event receive never gets data

EventSubscriber.subscribe() without the C++ extension only records the eventgroup ID in a local set — it doesn't subscribe over the network. The MessageReceiver._sync_queue never receives any messages, so receive_event() always times out.

4. Jumpstarter driver doesn't validate native extension availability

The SomeIp driver (driver.py) creates an OsipClient and calls its methods, but never checks whether opensomeip._bridge.HAS_NATIVE is True. All operations silently degrade to stubs returning fake data.

Impact

Operation Observed behavior Expected behavior
rpc_call() return_code=0, payload='' return_code=0, payload='01'
receive_event() TimeoutError SomeIpEventNotification(payload='43480000')
find_service() Returns [] (no services found) Returns service entries

The failure is silent — no errors, no warnings in Jumpstarter logs. RPC calls appear to succeed (return_code=0) but have no data. This makes debugging extremely difficult.

Proposed fix

In jumpstarter-driver-someip (this repo)

  1. Validate native extension at driver init: In SomeIp.__post_init__(), check opensomeip._bridge.HAS_NATIVE and raise a clear error if the C++ extension is not available:
from opensomeip._bridge import get_ext

def __post_init__(self):
    if get_ext() is None:
        raise RuntimeError(
            "opensomeip C++ extension is not available. "
            "The SOME/IP driver requires the native extension for real network I/O. "
            "See https://github.com/vtz/opensomeip-python#troubleshooting"
        )
    # ... rest of init
  1. Log a warning on empty RPC payloads for methods that are expected to return data (optional, defense-in-depth).

Upstream in opensomeip-python (vtz/opensomeip-python)

  1. RpcClient.call() stub should raise, not return fake data: The fallthrough path should raise RuntimeError("C++ extension required for RPC calls") instead of returning a synthetic response.

  2. Remove except Exception: pass: The blanket exception swallowing in the C++ code path should at minimum log the exception, or better, let it propagate.

Workaround

Ensure the opensomeip C++ extension is properly installed. On macOS, rebuild with the system compiler:

CC=/usr/bin/clang CXX=/usr/bin/clang++ \
  pip install --no-cache-dir --force-reinstall --no-binary=opensomeip opensomeip

Verify the extension loads:

python3 -W all -c "from opensomeip._bridge import get_ext; print('native:', get_ext() is not None)"

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions