Skip to content

read() silently returns default values on all failure paths with no mechanism for downstream error detection #390

@GaneshPatil7517

Description

@GaneshPatil7517

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_val

Critically, 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() check

Proposed 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_FOUND

This 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

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