Skip to content

IndexError: pop from empty list in check_signature_compatible when subject method has zero positional params #550

@HackedRico

Description

@HackedRico

Things to check first

  • I have searched the existing issues and didn't find my bug already reported there

  • I have checked that my bug is still present in the latest release

Typeguard version

4.5.1

Python version

3.14.4

What happened?

typeguard.check_type(value, SomeProtocol) crashed with IndexError: pop from empty list instead of returning or raising TypeCheckError.

File ".../typeguard/_checkers.py", line 769, in check_signature_compatible
    subject_args.pop(0)
IndexError: pop from empty list

The two pop(0) calls in check_signature_compatible :

if protocol_type == "instance":
protocol_args.pop(0)
# Remove the "self" parameter from the subject arguments to match
if subject_type == "instance":
subject_args.pop(0)

If inspect.signature(subject_attr) reports zero positional-or-keyword params and no *args, subject_args is empty and the pop blows up. Callable instances, partials, and other non-staticmethod/non-classmethod descriptors all hit this path.

What I expected was a TypeCheckError. The same arity mismatch with one more positional arg on the subject raises a clean TypeCheckError ("too few positional arguments"). Drop the count by one and it becomes IndexError. Equivalent mismatch, two different exception types, only one of which check_type is documented to raise, so callers catching TypeCheckError leak the IndexError through.

The fix I am running locally is to guard each pop:

if protocol_type == "instance" and protocol_args:
    protocol_args.pop(0)
if subject_type == "instance" and subject_args:
    subject_args.pop(0)

With that, the repro raises TypeCheckError and the rest of the test suite still passes for me.

Happy to PR with a regression test under tests/test_checkers.py::TestProtocol. If you would rather the empty case raise an explicit TypeCheckError earlier with a more specific message, let me know and I will rework.

How can we reproduce the bug?

Stdlib only, no third-party deps:

from typing import Protocol
from typeguard import check_type

class _ZeroArg:
    def __call__(self):
        return None

class Subject:
    method = _ZeroArg()

class P(Protocol):
    def method(self, x: int) -> None: ...

check_type(Subject(), P)
# IndexError: pop from empty list

Run on Python 3.14, typeguard 4.5.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions