Skip to content

Types: _coerce_decimal silently accepts bool → cryptic InvalidOperation #216

Closed

Description

@TexasCoding

Summary

bool is an int subclass in Python, so isinstance(True, (int, float)) is True and the value falls into the int/float branch of _coerce_decimal. Decimal(str(True)) then attempts Decimal('True') and raises decimal.InvalidOperation('[<class ConversionSyntax>]').

That is a confusing low-level error for a foot-gun (passing True for a price or count is almost always a typo — is_yes=True accidentally tabbed into count=True). The SDK should fail clearly.

Location

kalshi/types.py:13-28

Evidence

# kalshi/types.py:22-25
if isinstance(value, (int, float)):
    return Decimal(str(value))   # ← Decimal(str(True)) == Decimal('True') → InvalidOperation

Reproduction:

>>> from kalshi.types import _coerce_decimal
>>> _coerce_decimal(True)
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]

Recommended fix

Add an explicit bool check before the (int, float) branch:

def _coerce_decimal(value: Any) -> Decimal:
    if isinstance(value, Decimal):
        return value
    if isinstance(value, bool):
        raise TypeError(
            "Cannot convert bool to Decimal — bool is an int subclass, "
            "so this is almost always a typo (did you mean count=1?)."
        )
    if isinstance(value, (int, float)):
        return Decimal(str(value))
    ...

Mirror the check in to_decimal. Add unit test: test_coerce_decimal_rejects_bool.

Severity & category

medium / correctness

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions