{"record":{"id":"9a4a48bd40f1b491","repo":"pentaho/pentaho-kettle","slug":"tostring-couldn-t-convert-string-to-number-with-empty-format","errorCode":null,"errorMessage":"<toString()> : couldn't convert String to number with <empty format|format [getFormatMask()]>: unexpected character found at position <parsePosition.getErrorIndex() + 1> for value [<string>]","messagePattern":"<toString\\(\\)> : couldn't convert String to number with <empty format\\|format \\[getFormatMask\\(\\)\\]>: unexpected character found at position <parsePosition\\.getErrorIndex\\(\\) \\+ 1> for value \\[<string>\\]","errorType":"exception","errorClass":"KettleValueException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java","lineNumber":1029,"sourceCode":"    string = Const.trimToType( string, getTrimType() ); // see if trimming needs\n    // to be performed before\n    // conversion\n\n    if ( Utils.isEmpty( string ) ) {\n      return null;\n    }\n\n    try {\n      DecimalFormat format = getDecimalFormat( false );\n      Number number;\n      if ( lenientStringToNumber ) {\n        number = format.parse( string );\n      } else {\n        ParsePosition parsePosition = new ParsePosition( 0 );\n        number = format.parse( string, parsePosition );\n\n        if ( parsePosition.getIndex() < string.length() ) {\n          throw new KettleValueException( toString()\n              + \" : couldn't convert String to number with \" + ( ( getFormatMask() == null ) ? \"empty format\" : \"format [\" + getFormatMask() + \"]\" ) +  \": unexpected character found at position \"\n              + ( parsePosition.getErrorIndex() + 1 ) + \" for value [\" + string + \"]\" );\n        }\n\n      }\n\n      return new Double( number.doubleValue() );\n    } catch ( Exception e ) {\n      throw new KettleValueException( toString() + \" : couldn't convert String to number \", e );\n    }\n  }\n\n  @Override\n  public synchronized SimpleDateFormat getDateFormat() {\n    return getDateFormat( getType() );\n  }\n\n  private synchronized SimpleDateFormat getDateFormat( int valueMetaType ) {","sourceCodeStart":1011,"sourceCodeEnd":1047,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java#L1011-L1047","documentation":"Thrown by ValueMetaBase.convertStringToNumber when DecimalFormat.parse leaves unconsumed characters: ParsePosition.getIndex() < string.length() means trailing junk after the numeric portion. The message reports the 1-based position of the unexpected character, the format mask, and the input string.","triggerScenarios":"Calling getNumber()/convertStringToNumber() with a string like '123abc', '1 234,5 EUR', or a value that does not fully match getFormatMask() (grouping separators, currency symbols, or spaces in the wrong place) when no lenient 'format.parse(string)' path is used.","commonSituations":"Export files with trailing units ('kg', '%') or currency symbols; wrong decimal/grouping separator for the configured locale; extra whitespace or BOM at the end of the value; mask changed after the data format changed.","solutions":["Trim/clean the input string (remove trailing units, symbols, non-breaking spaces) before conversion","Set the correct format mask with setConversionMask matching the data (including grouping/decimal separators)","Set the proper locale via setConversionLocale so separators match","Pre-validate with a regex like ^[+-]?[0-9.,]+$ before calling getNumber"],"exampleFix":"// before\nDouble d = valueMeta.getNumber(\"1.234,56 kg\"); // throws at position 9\n// after\nString cleaned = raw.replaceAll(\"[^0-9,.-]\", \"\").trim();\nDouble d = valueMeta.getNumber(cleaned);","handlingStrategy":"validation","validationCode":"static boolean isFullyNumeric(String s, char groupSep, char decSep) {\n  if (s == null || s.isEmpty()) return false;\n  String t = s.trim();\n  String regex = \"^[+-]?[0-9\" + java.util.regex.Pattern.quote(String.valueOf(groupSep))\n      + java.util.regex.Pattern.quote(String.valueOf(decSep)) + \"]+$\";\n  return t.matches(regex);\n}","typeGuard":"static boolean parsesFully(java.text.DecimalFormat fmt, String s) {\n  java.text.ParsePosition pos = new java.text.ParsePosition(0);\n  Number n = fmt.parse(s, pos);\n  return n != null && pos.getIndex() == s.trim().length();\n}","tryCatchPattern":"try {\n  Number n = valueMeta.getNumber(str);\n} catch (KettleValueException e) {\n  log.error(\"Bad number '\" + str + \"': \" + e.getMessage());\n  routeToErrorRow(str, \"non-numeric trailing characters\");\n}","preventionTips":["Trim strings and strip currency symbols/units before conversion","Match the mask's decimal/grouping separators to the data's locale","Pre-validate with DecimalFormat + ParsePosition checking getIndex() == length","Enable transformation error handling to catch bad rows"],"tags":["number","parsing","decimalformat","kettle"],"backgroundTag":"invalid-argument-format","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}