stanfordnlp/CoreNLP · warning

Caught bad number:

Error message

Caught bad number: 

What it means

A logged warning in extractTimeExpressions: NumberNormalizer.findAndMergeNumbers threw a NumberFormatException while normalizing numeric tokens into NumerizedTokensAnnotation. The code logs 'Caught bad number: <message>' and sets an empty list so annotation can proceed without numbers. Text containing unparseable numeric expressions triggers it.

Solutions

  1. Inspect the logged message for the offending token and normalize/clean such numbers before annotation
  2. Ensure standard tokenization options are used (sutime expects CoreNLP tokenizer output)
  3. Upgrade CoreNLP; NumberNormalizer robustness has improved across versions
  4. Accept the degradation: the code already continues with an empty numbers list; only time expressions depending on those numbers are lost

Example fix

// before
// annotating raw noisy text directly
CoreDocument doc = new CoreDocument("raw OCR text 0000000000000000000.5.5");
pipeline.annotate(doc);
// after
String cleaned = raw.replaceAll("(?<=\\d)\\.(?=\\.|\\d*\\.\\d*\\.)", ""); // drop malformed number runs
CoreDocument doc = new CoreDocument(cleaned);
pipeline.annotate(doc);
Defensive patterns

Strategy: validation

Validate before calling

// pre-clean number-like tokens that break NumberNormalizer
String cleaned = text.replaceAll("\\b\\d{15,}\\b", "")           // absurdly long digit strings
                     .replaceAll("(?<=[0-9])[._](?=[._])", "");   // malformed decimal runs

Try / catch

try {
  pipeline.annotate(doc);
} catch (Exception e) {
  log.warn("Annotation pipeline issue", e); // NumberFormatException itself is caught by CoreNLP
}

Prevention

When it happens

Trigger: Calling extractTimeExpressions (or running the 'sutime' annotator) on a CoreMap whose tokenized text contains number-like strings NumberNormalizer cannot parse (e.g. very long digit strings, mixed alphanumeric tokens like '3rd-and-4', locale-odd numbers).

Common situations: Noisy user text, OCR output, tweets with strings like '100000000000000000000x'; also occurs when custom tokenization splits numbers in ways NumberNormalizer does not expect.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10). Data as JSON: /api/errors/a230a3b1d28a45c5. Report an issue: GitHub.

Appendix: source

Thrown at src/edu/stanford/nlp/time/TimeExpressionExtractorImpl.java:194

    if (refDateStr != null) {
      try {
        // TODO: have more robust parsing of document date?  docDate may not have century....
        // TODO: if docDate didn't change, we can cache the parsing of the docDate and not repeat it for every sentence
        refDate = SUTime.parseDateTime(refDateStr,true);
      } catch (Exception e) {
        throw new RuntimeException("Could not parse date string: [" + refDateStr + "]", e);
      }
    }
    return extractTimeExpressions(annotation, refDate, timeIndex);
  }

  public List<TimeExpression> extractTimeExpressions(CoreMap annotation, SUTime.Time refDate, SUTime.TimeIndex timeIndex) {
    if (!annotation.containsKey(CoreAnnotations.NumerizedTokensAnnotation.class)) {
      try {
        List<CoreMap> mergedNumbers = NumberNormalizer.findAndMergeNumbers(annotation);
        annotation.set(CoreAnnotations.NumerizedTokensAnnotation.class, mergedNumbers);
      } catch (NumberFormatException e) {
        logger.warn("Caught bad number: " + e.getMessage());
        annotation.set(CoreAnnotations.NumerizedTokensAnnotation.class, new ArrayList<>());
      }
    }

    List<? extends MatchedExpression> matchedExpressions = expressionExtractor.extractExpressions(annotation);
    List<TimeExpression> timeExpressions = new ArrayList<>(matchedExpressions.size());
    for (MatchedExpression expr : matchedExpressions) {
      // Make sure we have the correct type (instead of just MatchedExpression)
      //timeExpressions.add(TimeExpression.TimeExpressionConverter.apply(expr));

      // TODO: Fix the extraction pipeline so it creates TimeExpression instead of MatchedExpressions
      // For now, grab the time expression from the annotation (this is good, so we don't have duplicate copies)
      TimeExpression annoTe = expr.getAnnotation().get( TimeExpression.Annotation.class );
      if (annoTe != null) {
        timeExpressions.add(annoTe);
      }
    }
    // We cache the document date in the timeIndex

View on GitHub (pinned to 1b7edd19c4)