Skip to content

zmq.poll() Needs Better Argument Type Checking #77

@warnes

Description

@warnes

Summary

The zmq.poll() function in pbdZMQ expects its first argument to be a vector of socket pointers, but when a single socket pointer is passed (which is a common use case), it produces a cryptic error message that doesn't indicate the actual problem. The function should validate its arguments and provide a clear error message.

Expected Behavior

When calling zmq.poll(socket, ...) with a single socket pointer, the function should either:

  1. Accept the single socket and treat it as a vector of length 1, OR
  2. Provide a clear error message indicating that a vector is required

Actual Behavior

The function produces a cryptic error:

Error in LENGTH() or similar applied to externalptr object

This error message doesn't indicate that the issue is with the argument type to zmq.poll().

Reproducible Example

library(pbdZMQ)

# Create context and socket
context <- zmq.ctx.new()
socket <- zmq.socket(context, ZMQ.ST()$REQ)

# Connect to a dummy endpoint (doesn't need to be real for this error)
zmq.connect(socket, "tcp://localhost:5555")

# Send a message
zmq.send(socket, "Hello")

# This produces the cryptic error:
# Error in LENGTH() or similar applied to externalptr object
poll_result <- zmq.poll(socket, ZMQ.PO()$POLLIN, timeout = 1000)

# Clean up
zmq.close(socket)
zmq.ctx.destroy(context)

Correct Usage

The correct way to call zmq.poll() is with a vector:

# This works correctly:
poll_result <- zmq.poll(c(socket), ZMQ.PO()$POLLIN, timeout = 1000)

Suggested Fix

The zmq.poll() function should the type of the socket parameter, and convert wrap it in c() if it is an externalptr. (Better yet, add a class to external pointers returned by the package so the code can check that it's getting the right kind of pointer):

if(is(socket, "externalptr")) socket <- c(socket)

Impact

This issue makes debugging ZeroMQ applications unnecessarily difficult, as the error message doesn't point to the actual problem. It's particularly confusing for users coming from other ZeroMQ bindings where polling a single socket doesn't require wrapping it in a vector.

Environment

> sessionInfo()
R version 4.5.1 (2025-06-13)
Platform: x86_64-apple-darwin20
Running under: macOS Sequoia 15.5

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.5-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices datasets  utils     methods   base     

other attached packages:
[1] pbdZMQ_0.3-14

loaded via a namespace (and not attached):
[1] compiler_4.5.1    tools_4.5.1       rstudioapi_0.17.1 renv_1.1.4       
> 

Additional Notes

This issue was discovered while implementing the ZeroMQ "Lazy Pirate" pattern. The error occurs in common client-server communication scenarios where polling a single socket for responses is a typical use case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions