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
- Inspect the logged message for the offending token and normalize/clean such numbers before annotation
- Ensure standard tokenization options are used (sutime expects CoreNLP tokenizer output)
- Upgrade CoreNLP; NumberNormalizer robustness has improved across versions
- 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
- Feed CoreNLP its own tokenizer output instead of pre-split strings
- Clean OCR/social-media noise before annotation
- Check the 'Caught bad number' logs to build a cleaning allowlist for your domain
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 timeIndexView on GitHub (pinned to 1b7edd19c4)