bazelbuild/bazel · error · NumberFormatException

cannot infer base when string begins with a 0: %s

Error message

cannot infer base when string begins with a 0: %s

What it means

StarlarkInt.parse cannot choose a base when the caller passes base=0 ('auto') and the digit string begins with '0' (and is longer than one char). A leading 0 is ambiguous between octal (0755) and decimal-with-padding (0123); Starlark (unlike Python's int, which defaults to decimal) refuses to guess and throws NumberFormatException with this message. Note the 0x/0o/0b prefixes are handled earlier; this path is for unprefixed strings.

Source

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

        prefixBase = 2;
      } else if (c == 'o' || c == 'O') {
        prefixBase = 8;
      } else if (c == 'x' || c == 'X') {
        prefixBase = 16;
      }
      if (prefixBase != 0) {
        if (base == 0 || base == prefixBase) {
          base = prefixBase;
          digits = s.substring(2); // strip prefix
        }
      }
    }

    // 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)));
    }

View on GitHub (pinned to e6e199d060)

Solutions

  1. Strip leading zeros before converting: int(s.lstrip("0") or "0").
  2. Pass the base explicitly when the value is octal: int("0755", 8).
  3. Change the producer to emit unpadded decimal strings.

Example fix

# before
mode = int("0644")

# after
mode = int("0644", 8)  # if octal was intended
# or
mode = int("0644".lstrip("0") or "0")  # if decimal 644 was intended
Defensive patterns

Strategy: validation

Validate before calling

def parse_decimal(s):
    s = s.strip()
    if len(s) > 1 and s.startswith("0") and not s.startswith("0x") and not s.startswith("0o") and not s.startswith("0b"):
        s = s.lstrip("0") or "0"  # zero-padded decimal
    return int(s, 10)

Type guard

def has_leading_zero_ambiguity(s):
    return len(s) > 1 and s[0] == "0" and s[1] not in "xob"

Prevention

When it happens

Trigger: int("0123"), int("0755"), int("00") — any call with base omitted (base defaults to 10 only after this check; base=0/auto with leading-zero digits). Commonly hit when porting Python int("0755") code.

Common situations: Parsing zero-padded numbers from fixed-width formats (timestamps '0930', zip codes, permissions '0644', IDs with leading zeros); Python-to-Starlark port assuming int() strips leading zeros.

Related errors


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