{"record":{"id":"8bc4f480b18d37da","repo":"apache/iceberg","slug":"unknown-transform-class-s","errorCode":null,"errorMessage":"Unknown transform class %s","messagePattern":"Unknown transform class (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/org/apache/iceberg/transforms/PartitionSpecVisitor.java","lineNumber":149,"sourceCode":"        || transform == Timestamps.NANOS_TO_MONTH\n        || transform instanceof Months) {\n      return visitor.month(field.fieldId(), sourceName, field.sourceId());\n    } else if (transform == Dates.DAY\n        || transform == Timestamps.MICROS_TO_DAY\n        || transform == Timestamps.NANOS_TO_DAY\n        || transform instanceof Days) {\n      return visitor.day(field.fieldId(), sourceName, field.sourceId());\n    } else if (transform == Timestamps.MICROS_TO_HOUR\n        || transform == Timestamps.NANOS_TO_HOUR\n        || transform instanceof Hours) {\n      return visitor.hour(field.fieldId(), sourceName, field.sourceId());\n    } else if (transform instanceof VoidTransform) {\n      return visitor.alwaysNull(field.fieldId(), sourceName, field.sourceId());\n    } else if (transform instanceof UnknownTransform) {\n      return visitor.unknown(field.fieldId(), sourceName, field.sourceId(), transform.toString());\n    }\n\n    throw new UnsupportedOperationException(\n        String.format(\"Unknown transform class %s\", field.transform().getClass().getName()));\n  }\n}\n","sourceCodeStart":131,"sourceCodeEnd":153,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/api/src/main/java/org/apache/iceberg/transforms/PartitionSpecVisitor.java#L131-L153","documentation":"PartitionSpecVisitor.visit() throws this after exhausting its instanceof dispatch over all known transform classes (Identity, Bucket, Truncate, Years/Months/Days/Hours, VoidTransform, UnknownTransform and the legacy Dates/Timestamps singletons). Hitting it means a PartitionField's transform is an object of a class the visitor dispatcher does not recognize at all — distinct from the per-transform default hooks, this is a dispatch-level failure inside Iceberg itself.","triggerScenarios":"A PartitionField carries a Transform instance outside the known set — typically a user-defined Transform subclass implementation, or a class from a different/patched Iceberg version placed on the classpath, passed to PartitionSpecVisitor.visit(schema, field, visitor).","commonSituations":"Custom Transform implementations registered via custom serialization on the writer side; mixed Iceberg jar versions (shaded vs non-shaded, vendor forks) so the same logical transform fails instanceof checks; third-party catalogs injecting custom transform objects into specs.","solutions":["Ensure all Iceberg modules use the same version — check the classpath for duplicate/conflicting iceberg-api jars (mvn dependency:tree)","If you ship a custom Transform subclass, it cannot be dispatched here; convert the spec field to a standard transform or extend the visitor dispatch path upstream","Reproduce with field.transform().getClass().getName() from the message to identify the offending class and its jar"],"exampleFix":"// before\nTransform myTransform = new MyCustomTransform(); // not dispatched by visit()\n// after\nTransforms.identity() // or another standard transform (bucket/truncate/year/...) known to visit()","handlingStrategy":"validation","validationCode":"// Detect non-standard transform classes before dispatch:\nSet<String> known = Set.of(\"Identity\", \"Bucket\", \"Truncate\", \"Years\", \"Months\", \"Days\",\n    \"Hours\", \"VoidTransform\", \"UnknownTransform\");\nfor (PartitionField f : spec.fields()) {\n  String cls = f.transform().getClass().getSimpleName();\n  if (!known.contains(cls)) throw new IllegalStateException(\"Non-standard transform: \" + cls);\n}","typeGuard":"if (!(field.transform() instanceof Identity || field.transform() instanceof Bucket\n    || field.transform() instanceof Truncate || field.transform() instanceof VoidTransform\n    || field.transform() instanceof UnknownTransform\n    || field.transform() instanceof Years || field.transform() instanceof Months\n    || field.transform() instanceof Days || field.transform() instanceof Hours)) {\n  throw new IllegalStateException(\"Unsupported transform class: \" + field.transform().getClass());\n}","tryCatchPattern":"try {\n  PartitionSpecVisitor.visit(spec, visitor);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().startsWith(\"Unknown transform class\")) {\n    throw new IllegalStateException(\"Mixed Iceberg versions or custom transform on classpath\", e);\n  }\n  throw e;\n}","preventionTips":["Keep all iceberg-* artifacts at one version; run mvn dependency:tree to detect conflicts","Avoid custom Transform subclasses in persisted specs — stick to Transforms.* factories","Log field.transform().getClass().getName() when this fires to identify the offending jar"],"tags":["java","classloader","version-skew","internal"],"backgroundTag":"internal-invariant-violation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}