bazelbuild/bazel · error · NumberFormatException

invalid base-%d literal: %s

Error message

invalid base-%d literal: %s

What it means

StarlarkInt.parse's parse-failure error: it is raised in two places — first when, after stripping one leading sign and any 0x/0o/0b prefix, the digit string still starts with '+' or '-' (Long.parseLong/BigInteger must not see an embedded second sign), and again when neither Long.parseLong nor BigInteger can parse the digits in the requested base. The message includes the base and the repr of the original input.

Source

Thrown at src/main/java/net/starlark/java/eval/StarlarkInt.java:159

    // No prefix, no base? Use decimal.
    if (digits == s && base == 0) {
      // Don't infer base when input starts with '0' due to octal/decimal ambiguity.
      if (s.length() > 1 && s.charAt(0) == '0') {
        throw new NumberFormatException(
            "cannot infer base when string begins with a 0: "
                + Starlark.repr(stringForErrors, StarlarkSemantics.DEFAULT));
      }
      base = 10;
    }
    if (base < 2 || base > 36) {
      throw new NumberFormatException(
          String.format("invalid base %d (want 2 <= base <= 36)", base));
    }

    // Do not allow Long.parseLong and new BigInteger to accept another +/- sign.
    if (digits.startsWith("+") || digits.startsWith("-")) {
      throw new NumberFormatException(
          String.format(
              "invalid base-%d literal: %s",
              base, Starlark.repr(stringForErrors, StarlarkSemantics.DEFAULT)));
    }

    StarlarkInt result;
    try {
      result = StarlarkInt.of(Long.parseLong(digits, base));
    } catch (NumberFormatException unused1) {
      try {
        result = StarlarkInt.of(new BigInteger(digits, base));
      } catch (NumberFormatException unused2) {
        throw new NumberFormatException(
            String.format(
                "invalid base-%d literal: %s",
                base, Starlark.repr(stringForErrors, StarlarkSemantics.DEFAULT)));
      }
    }

View on GitHub (pinned to e6e199d060)

Solutions

  1. Normalize input before parsing: strip whitespace, collapse duplicate signs, remove separators: s = s.replace("_", "").strip().
  2. Apply the sign yourself: int(s.lstrip("+-"), base) * (-1 if s.startswith("-") else 1).
  3. Check the digit alphabet matches the base (base 8 has no 8/9; base 16 allows a-f) and remove 0x/0o/0b when passing an explicit base.

Example fix

# before
n = int(raw, 16)  # raw = "-0xff" or "0xff" with base given is fine, but "ff_00" or " -0xff" fails

# after
clean = raw.strip().replace("_", "")
n = int(clean, 16)
Defensive patterns

Strategy: validation

Validate before calling

import re  # where available; otherwise manual scan
def looks_like_int(s, base=10):
    s = s.strip().replace("_", "")
    return re.match(r"^[+-]?(0[xob])?[0-9a-zA-Z]+$", s) != None  # then also check digits < base

Prevention

When it happens

Trigger: int("--5"), int("+-3"), int("0x-10", 16) (sign after prefix), int("abc", 10), int("129", 8) (digit 9 invalid in base 8), int("1_000") (underscores unsupported), int(" "+"5") — whitespace not stripped.

Common situations: Parsing numbers with sign conventions the parser disallows (double negatives for 'minus a negative'); digit separators like 1_000 copied from Java/Python 3.6; hex strings with 0x prefix plus explicit base 16 AND a sign in the wrong place; whitespace or units left in the string ("5px", " 5").

Related errors


AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14). Data as JSON: /api/errors/687947411c3e209e. Report an issue: GitHub.