Skip to content

FAQ

Frequently Asked Questions

Is Pika thread safe?

Pika provides Connection for multi-threaded applications. It wraps SelectConnection with a dedicated IOLoop thread and exposes a blocking API that is safe to call from any number of threads simultaneously. Consumer callbacks run on a per-channel worker thread, so blocking work inside a callback needs no manual callback scheduling and heartbeats keep flowing while it runs. A callback that cannot keep pace with deliveries eventually fills that thread's bounded queue and blocks the IOLoop; see work queue bounds.

import pika

conn = pika.Connection(pika.ConnectionParameters('localhost'))
ch = conn.channel()
# RabbitMQ 4.3 and later refuse a queue that is neither durable nor exclusive.
ch.queue_declare('q', durable=True)
# safe to call from any thread
ch.basic_publish(exchange='', routing_key='q', body=b'hello')

The other connection adapters (BlockingConnection, SelectConnection, AsyncioConnection, etc.) are not thread-safe. Each connection instance is confined to the thread that created it. The only safe cross-thread operation on these adapters is calling add_callback_threadsafe to schedule a callback in the connection's IOLoop thread. See connection adapters for details.

Is Pika fork safe?

No. No pika connection survives os.fork(), and that includes multiprocessing under the fork start method, which is the default on Linux before Python 3.14. Create the connection in the child, after the fork, or start the child with the spawn method.

The child inherits the parent's socket descriptor but none of its threads, so for pika.Connection the IOLoop thread and every consumer worker exist only in the parent: nothing in the child reads from the socket or sends heartbeats, and a blocking call there waits for a reply that no thread will deliver. Both processes can still write to that one socket, and interleaved frames corrupt the connection for the parent as well. Using the connection in the child is undefined, whatever the state of its attributes suggests.

Why does mypy report new errors on my consumer callback after upgrading?

Pika ships a py.typed marker, so a type checker reads pika's own annotations. pika.spec is generated code, and it was previously excluded from pika's mypy configuration, which left every field of a decoded frame typed as Any. Any silences all checking, so code like this passed:

def on_message(ch, method, properties, body):
    ch.basic_ack(delivery_tag=method.delivery_tag)

pika.spec is now annotated and checked, so the same line reports:

error: Argument "delivery_tag" to "basic_ack" of "BlockingChannel"
has incompatible type "int | None"; expected "int"

The errors are accurate rather than newly introduced behavior. Two properties of the generated code cause them:

  • Fields are optional. Every generated constructor defaults its arguments to None, so delivery_tag is int | None. A frame decoded off the wire always carries the field, but the type cannot express that.
  • String fields may be bytes. AMQP shortstr is a length-prefixed byte string with no declared encoding. Pika decodes it as UTF-8 and falls back to the raw bytes when that fails, so routing_key, exchange, and consumer_tag are str | bytes | None.

An assert narrows the optional away, and isinstance narrows the bytes:

def on_message(ch: BlockingChannel, method: Basic.Deliver,
               properties: BasicProperties, body: bytes) -> None:
    assert method.delivery_tag is not None
    ch.basic_ack(delivery_tag=method.delivery_tag)

    routing_key = method.routing_key
    assert isinstance(routing_key, str)
    ch.basic_publish(exchange='', routing_key=routing_key, body=body)

Narrowing only the optional is not always enough for a string field, since str | bytes remains. Whether that matters depends on the operation: routing_key.upper() checks either way, because bytes also has upper(), while passing the value back into basic_publish, using it as a dict[str, ...] key, or concatenating it with a str all require the isinstance narrowing above.

Use typing.cast instead of assert where the check should carry no runtime cost. Note that assert statements are stripped under python -O.

pyright reported most of these already, so its users see little change.

How do I report a bug with Pika?

The main Pika repository is hosted on GitHub, and we use the issue tracker at github.com/pika/pika/issues.

Is there a mailing list for Pika?

Yes. Pika's mailing list is available on Google Groups, and the email address is pika-python@googlegroups.com.

Traditionally, questions about Pika have also been asked on the RabbitMQ mailing list.

How can I contribute to Pika?

You can fork the project on GitHub and open pull requests when you believe you have something solid to add to the main repository.