-
Notifications
You must be signed in to change notification settings - Fork 33
Description
Problem Description
Every failure path in read() file not found, empty after max retries, parse error, ZMQ timeout returns the initstr_val default. There is no way for the calling control loop to distinguish between "received real data that happens to equal the default" and "failed to read, got fallback." In a control system, feeding stale/default values into a controller without the controller knowing they are stale can cause unsafe actuation.
Technical Analysis
Python (concore.py, read()):
# On FileNotFoundError:
ins = str(initstr_val)
s += ins # <-- appends default to s, so unchanged() may see it as "new data"
# On max retries:
return default_return_val
# On parse error:
return default_return_valCritically, the FileNotFoundError path appends the default to s, which means unchanged() will see this as a data change and break the spin-wait loop allowing the node to proceed with default data as if it were real input.
The C++ version has the same behavior:
catch (...) { ins = initstr; } // fallback to initstr, no error flag
s += ins; // contributes to unchanged() checkProposed Improvement
Return a status-bearing result type instead of raw values:
# Option: return (data, is_valid) tuple
def read(port, name, initstr):
# ... on success:
return (payload, True)
# ... on failure:
return (default, False)Or provide a per-read error counter that nodes can check:
concore.last_read_status # SUCCESS, TIMEOUT, PARSE_ERROR, FILE_NOT_FOUNDThis is backward-compatible if callers that don't check the status simply ignore the second return value.
Impact
- Reliability: Controllers operate on stale data without knowing safety risk in neuromodulation
- Real-time: No distinction between "slow producer" and "dead producer"
- Julia impl: Opportunity to use Julia's
Union{T, Nothing}or error types from the start - Cross-language: All implementations share this gap; needs protocol-level error signaling