python/cpython · error · ValueError

Invalid sign. The first value in the tuple should be an int

Error message

Invalid sign.  The first value in the tuple should be an integer; either 0 for a positive number or 1 for a negative number.

What it means

When constructing a Decimal from a 3-element sequence, the first element must be a Python int equal to 0 (positive) or 1 (negative). The isinstance(value[0], int) check also deliberately rejects floats like 1.0 and strings like '1'. This ValueError signals a malformed sign field in the decimal triple.

Source

Thrown at Lib/_pydecimal.py:561

            return self

        # From an internal working value
        if isinstance(value, _WorkRep):
            self._sign = value.sign
            self._int = str(value.int)
            self._exp = int(value.exp)
            self._is_special = False
            return self

        # tuple/list conversion (possibly from as_tuple())
        if isinstance(value, (list,tuple)):
            if len(value) != 3:
                raise ValueError('Invalid tuple size in creation of Decimal '
                                 'from list or tuple.  The list or tuple '
                                 'should have exactly three elements.')
            # process sign.  The isinstance test rejects floats
            if not (isinstance(value[0], int) and value[0] in (0,1)):
                raise ValueError("Invalid sign.  The first value in the tuple "
                                 "should be an integer; either 0 for a "
                                 "positive number or 1 for a negative number.")
            self._sign = value[0]
            if value[2] == 'F':
                # infinity: value[1] is ignored
                self._int = '0'
                self._exp = value[2]
                self._is_special = True
            else:
                # process and validate the digits in value[1]
                digits = []
                for digit in value[1]:
                    if isinstance(digit, int) and 0 <= digit <= 9:
                        # skip leading zeros
                        if digits or digit != 0:
                            digits.append(digit)
                    else:
                        raise ValueError("The second value in the tuple must "

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Normalize the sign to int 0 or 1: sign = 1 if value < 0 else 0
  2. If the sign arrived as a string from JSON/CSV, convert it explicitly: sign = int(sign_str)
  3. Validate the triple shape before calling Decimal (see defense below)

Example fix

// before
sign = -1 if x < 0 else 0
d = Decimal((sign, digits, exp))  # ValueError for negatives

// after
sign = 1 if x < 0 else 0
d = Decimal((sign, digits, exp))
Defensive patterns

Strategy: validation

Validate before calling

def normalize_triple(sign, digits, exp):
    sign = int(sign)
    if sign not in (0, 1):
        sign = 1 if sign else 0  # maps -1/other truthy to 1
    return (sign, digits, exp)

Type guard

def valid_sign(s) -> bool:
    return isinstance(s, int) and not isinstance(s, bool) and s in (0, 1)

Try / catch

try:
    d = Decimal(triple)
except ValueError as e:
    if 'Invalid sign' in str(e):
        triple = (1 if triple[0] else 0, triple[1], triple[2])
        d = Decimal(triple)

Prevention

When it happens

Trigger: Decimal((2, (1,), 0)); Decimal((-1, (1,), 0)); Decimal(('0', (1,), 0)); Decimal((1.0, (1,), 0)) (float, not int); Decimal((True, (1,), 0)) actually passes because bool subclasses int and True == 1.

Common situations: Deserializing a decimal triple from JSON where the sign became a string or was computed as -1/other values; hand-building triples from sign math that yields -1 for negatives instead of 1.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/9127d3ba8c11212c. Report an issue: GitHub.