Skip to content

False-valued callable callbacks are treated as missing #256

Open

Description

@HuaTNA

Actual behavior

A callable object with a false truth value is selected and marked as called, but never invoked. For example, an initially empty list that records requests in __call__ causes httpx.TimeoutException with an "Already matched" response instead of returning its response.

Both _handle_request() and _handle_async_request() test if callback: after _get_callback() returns. That return type uses None for no callback; a callable can legitimately be false-valued.

Found during analysis of callback dispatch and reproduced locally through the public httpx_mock fixture and a real HTTPX client. This is not a claimed production incident.

Expected behavior

Invoke the registered matching callable regardless of its truth value. Continue treating None as no match and validating that the callback returns httpx.Response.

Steps to reproduce

import httpx

class RecordingCallback(list):
    def __call__(self, request):
        self.append(request)
        return httpx.Response(201, text="recorded")

def test_recording_callback(httpx_mock):
    callback = RecordingCallback()
    httpx_mock.add_callback(callback)
    with httpx.Client() as client:
        response = client.get("https://example.test")
    assert response.status_code == 201
    assert len(callback) == 1

Before the callback's first call, the list is empty and therefore false. The callback is skipped, leaving the recorder empty and raising the timeout. The same occurs with AsyncClient; asynchronous callable objects are affected too.

Environment: current develop at c2187d646e5dcac5795d71a7ada8d1021ec02a46 (package version 0.36.2), CPython 3.13.13, HTTPX 0.28.1, pytest 9.1.1, pytest-asyncio 1.4.1, macOS 27.2 arm64. No external server was contacted.

I checked the open and closed callback/callable/falsey/falsy reports and current PRs; #250 adds request predicates rather than changing response-callback presence checks. A narrow fix is to use callback is not None in both dispatch paths.

AI disclosure: an autonomous Codex assistant investigated and reproduced this issue under the submitting account's authorization; it has not received independent human review.

Activity

  1. Colin-b commented on Oct 27, 2026

    @Colin-b
    Owner

    Hi, can you elaborate on the use case for this?
    Why do you need to record requests, if all you want to assert is that your callback was called once, it is already performed by httpx_mock by default.

  2. HuaTNA commented on Oct 27, 2026

    @HuaTNA
    Author

    You're right that httpx_mock already records requests and checks callback usage. The list-based recorder was just a minimal reproducer from code inspection, rather than a requirement for additional request-recording functionality.

    The distinction I was testing is that an object can implement __call__ while being false when empty. _get_callback() returns that matched callable rather than None, but both handlers skip it at if callback. #257 changes those two presence checks so the callable is invoked.

    I don't have a production request-recording scenario to add beyond this minimal example; I wanted to make that discovery context clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions