Summary
When the C++ extension (_opensomeip) fails to load, RpcClient.call() returns a synthetic Message with return_code=E_OK and empty payload instead of raising an error. Transport.send() silently drops the message. This makes the library appear to work while doing nothing on the network, causing silent data loss for all downstream consumers (e.g. jumpstarter-driver-someip).
Reported downstream at jumpstarter-dev/jumpstarter#628.
Affected code
src/opensomeip/rpc.py — RpcClient.call()
The stub fallthrough path fabricates a success response:
# When self._cpp is None (no C++ extension):
request = Message(...)
self._transport.send(request) # no-op — no socket opened
return Message(
message_id=method_id,
request_id=request.request_id,
message_type=MessageType.RESPONSE,
return_code=ReturnCode.E_OK,
# payload defaults to b"" — caller thinks RPC succeeded
)
Additionally, the C++ code path has a blanket except Exception: pass that silently swallows all errors and falls through to the same stub:
if self._cpp is not None:
try:
result = self._cpp.call_method_sync(...)
if int(result.result) == 0:
return Message(...) # only returns on success
except Exception:
pass # ← ALL errors silently swallowed, falls through to stub
src/opensomeip/transport.py — Transport.send()
When the C++ extension is unavailable, send() is a silent no-op:
def send(self, message, endpoint=None):
if not self._running:
raise TransportError("Transport is not running")
if self._cpp is not None:
# ... sends via native transport
# When cpp is None: does absolutely nothing, no error
src/opensomeip/events.py — EventSubscriber.subscribe()
Without the C++ extension, subscribing only records the eventgroup ID locally — no actual subscription happens over the network. The notification MessageReceiver never gets any messages, causing all receive_event() callers to time out.
Observed impact
All of these produce identical symptoms regardless of whether the extension is missing or the network is broken:
| Operation |
Observed |
Expected |
client.call(method_id, payload) |
Message(return_code=E_OK, payload=b"") |
Message with actual response payload, or RpcError |
transport.send(message) |
Silent no-op |
TransportError |
event_subscriber.subscribe(eg_id) |
Local-only, no network |
Actual SD subscription |
receive_event(timeout=5) |
Always times out |
Event notifications from the ECU |
The ImportWarning emitted by _bridge.py is filtered by Python's default warning filters, making the degradation invisible in production.
Proposed fix
1. RpcClient.call() — raise instead of faking a response
def call(self, method_id, payload=b"", *, timeout=5.0):
if not self._running:
raise RpcError("RPC client is not running")
if self._cpp is not None:
try:
...
result = self._cpp.call_method_sync(...)
if int(result.result) == 0:
return Message(...)
raise RpcError(f"RPC call failed with result code {result.result}")
except RpcError:
raise
except Exception as exc:
raise RpcError(f"Native RPC call failed: {exc}") from exc
raise RpcError(
"Cannot perform RPC: opensomeip C++ extension is not available. "
"See https://github.com/vtz/opensomeip-python#troubleshooting"
)
2. Transport.send() — raise when no native transport
def send(self, message, endpoint=None):
if not self._running:
raise TransportError("Transport is not running")
if self._cpp is None:
raise TransportError(
"Cannot send: native transport is not available. "
"See https://github.com/vtz/opensomeip-python#troubleshooting"
)
...
3. EventSubscriber.subscribe() — raise when no native events
Same pattern: raise ConfigurationError or RuntimeError when self._cpp is None and a real subscription is attempted.
4. Documentation update for macOS users
The existing Troubleshooting section in README.md documents the macOS libc++ ABI issue and the "Silent no-op transport" behavior. However:
- The workaround (
CC=/usr/bin/clang CXX=/usr/bin/clang++ pip install ...) should be made more prominent (e.g. a callout/admonition at the top of the Quick Start section)
- Add a "Verify your installation" section:
### Verify native extension
After installation, confirm the C++ extension loads:
\`\`\`bash
python3 -c "from opensomeip._bridge import get_ext; ext = get_ext(); print('native:', ext is not None)"
\`\`\`
If this prints `native: False`, see the Troubleshooting section below.
\`\`\`
## Backward compatibility
Code that relied on the silent stub behavior (e.g. unit tests that import opensomeip without the C++ extension) will now get exceptions. This is intentional — the stubs were never producing correct results. Tests that need stub behavior should mock the transport/RPC layer explicitly.
## Related
- [jumpstarter-dev/jumpstarter#628](https://github.com/jumpstarter-dev/jumpstarter/issues/628) — downstream report of empty payloads
- Troubleshooting section in README.md — documents the macOS extension loading issue
Summary
When the C++ extension (
_opensomeip) fails to load,RpcClient.call()returns a syntheticMessagewithreturn_code=E_OKand empty payload instead of raising an error.Transport.send()silently drops the message. This makes the library appear to work while doing nothing on the network, causing silent data loss for all downstream consumers (e.g.jumpstarter-driver-someip).Reported downstream at jumpstarter-dev/jumpstarter#628.
Affected code
src/opensomeip/rpc.py—RpcClient.call()The stub fallthrough path fabricates a success response:
Additionally, the C++ code path has a blanket
except Exception: passthat silently swallows all errors and falls through to the same stub:src/opensomeip/transport.py—Transport.send()When the C++ extension is unavailable,
send()is a silent no-op:src/opensomeip/events.py—EventSubscriber.subscribe()Without the C++ extension, subscribing only records the eventgroup ID locally — no actual subscription happens over the network. The notification
MessageReceivernever gets any messages, causing allreceive_event()callers to time out.Observed impact
All of these produce identical symptoms regardless of whether the extension is missing or the network is broken:
client.call(method_id, payload)Message(return_code=E_OK, payload=b"")Messagewith actual response payload, orRpcErrortransport.send(message)TransportErrorevent_subscriber.subscribe(eg_id)receive_event(timeout=5)The
ImportWarningemitted by_bridge.pyis filtered by Python's default warning filters, making the degradation invisible in production.Proposed fix
1.
RpcClient.call()— raise instead of faking a response2.
Transport.send()— raise when no native transport3.
EventSubscriber.subscribe()— raise when no native eventsSame pattern: raise
ConfigurationErrororRuntimeErrorwhenself._cpp is Noneand a real subscription is attempted.4. Documentation update for macOS users
The existing Troubleshooting section in
README.mddocuments the macOS libc++ ABI issue and the "Silent no-op transport" behavior. However:CC=/usr/bin/clang CXX=/usr/bin/clang++ pip install ...) should be made more prominent (e.g. a callout/admonition at the top of the Quick Start section)