{"record":{"id":"da18cdb4417dce20","repo":"apache/iceberg","slug":"expected-truncation-col-to-be-tinyint-shortint-i-da18cd","errorCode":null,"errorMessage":"Expected truncation col to be tinyint, shortint, int, bigint, decimal, string, or binary","messagePattern":"Expected truncation col to be tinyint, shortint, int, bigint, decimal, string, or binary","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/functions/TruncateFunction.java","lineNumber":93,"sourceCode":"\n    DataType valueType = valueField.dataType();\n    if (valueType instanceof ByteType) {\n      return new TruncateTinyInt();\n    } else if (valueType instanceof ShortType) {\n      return new TruncateSmallInt();\n    } else if (valueType instanceof IntegerType) {\n      return new TruncateInt();\n    } else if (valueType instanceof LongType) {\n      return new TruncateBigInt();\n    } else if (valueType instanceof DecimalType) {\n      return new TruncateDecimal(\n          ((DecimalType) valueType).precision(), ((DecimalType) valueType).scale());\n    } else if (valueType instanceof StringType) {\n      return new TruncateString();\n    } else if (valueType instanceof BinaryType) {\n      return new TruncateBinary();\n    } else {\n      throw new UnsupportedOperationException(\n          \"Expected truncation col to be tinyint, shortint, int, bigint, decimal, string, or binary\");\n    }\n  }\n\n  @Override\n  public String description() {\n    return name()\n        + \"(width, col) - Call Iceberg's truncate transform\\n\"\n        + \"  width :: width for truncation, e.g. truncate(10, 255) -> 250 (must be an integer)\\n\"\n        + \"  col :: column to truncate (must be an integer, decimal, string, or binary)\";\n  }\n\n  @Override\n  public String name() {\n    return \"truncate\";\n  }\n\n  public abstract static class TruncateBase<T> extends BaseScalarFunction<T> {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/functions/TruncateFunction.java#L75-L111","documentation":"Iceberg's truncate(value, width) function supports value columns of tinyint, smallint, int, bigint, decimal, string, or binary only. Value columns of any other type (double, float, boolean, date, timestamp, complex types) have no truncation implementation, so bind() throws this UnsupportedOperationException during query analysis.","triggerScenarios":"Calling truncate(double_col, 2), truncate(bool_col, 1), truncate(ts_col, 10), or truncating struct/array/map columns.","commonSituations":"Truncating floats/doubles for bucket-like partitioning; attempting time truncation with truncate() instead of the dedicated years/months/days/hours functions; schema type drift to unsupported types.","solutions":["Cast the value to a supported type, e.g. truncate(CAST(d AS DECIMAL(10,4)), 2) for doubles.","For temporal truncation use the dedicated functions: years(), months(), days(), hours().","Pick a supported column (numeric, decimal, string, binary) for the partition transform.","For doubles, consider scaling to bigint: truncate(CAST(d * 100 AS BIGINT), 1)."],"exampleFix":"// before\nSELECT truncate(price, 2) FROM t  -- price is DOUBLE\n// after\nSELECT truncate(CAST(price AS DECIMAL(10,2)), 2) FROM t","handlingStrategy":"validation","validationCode":"// Spark Scala\nval dt = df.schema(\"value_col\").dataType.catalogString.toLowerCase\nval supported = Set(\"tinyint\",\"smallint\",\"int\",\"bigint\",\"decimal\",\"string\",\"binary\")\nrequire(supported.exists(s => dt.startsWith(s)), s\"truncate() does not support value type: $dt\")","typeGuard":"def isTruncatableType(dt: org.apache.spark.sql.types.DataType): Boolean = dt match {\n  case _: org.apache.spark.sql.types.DecimalType => true\n  case t => Set(\n    org.apache.spark.sql.types.ByteType,\n    org.apache.spark.sql.types.ShortType,\n    org.apache.spark.sql.types.IntegerType,\n    org.apache.spark.sql.types.LongType,\n    org.apache.spark.sql.types.StringType,\n    org.apache.spark.sql.types.BinaryType).contains(t)\n}","tryCatchPattern":"try {\n  df.select(expr(\"truncate(value_col, 5)\"))\n} catch {\n  case e: UnsupportedOperationException if e.getMessage.contains(\"truncation col\") =>\n    throw new IllegalArgumentException(\"truncate() value column type unsupported; cast or use days()/hours() for temporal\", e)\n}","preventionTips":["Never truncate float/double — cast to DECIMAL or scale to BIGINT first","Use years()/months()/days()/hours() for date/timestamp truncation, not truncate()","Inspect column types with printSchema before building partition transforms","Validate partition specs against supported truncation types at table creation"],"tags":["spark","sql-function","unsupported-type","bind"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}