{"record":{"id":"52979042b1be0677","repo":"apache/iceberg","slug":"invalid-primary-key-s-column-s-is-not-a-phy","errorCode":null,"errorMessage":"Invalid primary key '%s'. Column '%s' is not a physical column.","messagePattern":"Invalid primary key '(.+?)'\\. Column '(.+?)' is not a physical column\\.","errorType":"validation","errorClass":"org.apache.flink.table.api.ValidationException","httpStatus":null,"severity":"error","filePath":"flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/FlinkSchemaUtil.java","lineNumber":365,"sourceCode":"\n    if (!duplicateColumns.isEmpty()) {\n      throw new ValidationException(\n          String.format(\n              \"Invalid primary key '%s'. A primary key must not contain duplicate columns. Found: %s\",\n              primaryKey.getName(), duplicateColumns));\n    }\n\n    for (String columnName : primaryKey.getColumns()) {\n      Column column = columnsByNameLookup.get(columnName);\n      if (column == null) {\n        throw new ValidationException(\n            String.format(\n                \"Invalid primary key '%s'. Column '%s' does not exist.\",\n                primaryKey.getName(), columnName));\n      }\n\n      if (!column.isPhysical()) {\n        throw new ValidationException(\n            String.format(\n                \"Invalid primary key '%s'. Column '%s' is not a physical column.\",\n                primaryKey.getName(), columnName));\n      }\n\n      final LogicalType columnType = column.getDataType().getLogicalType();\n      if (columnType.isNullable()) {\n        throw new ValidationException(\n            String.format(\n                \"Invalid primary key '%s'. Column '%s' is nullable.\",\n                primaryKey.getName(), columnName));\n      }\n    }\n  }\n}\n","sourceCodeStart":347,"sourceCodeEnd":381,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/FlinkSchemaUtil.java#L347-L381","documentation":"A primary key column must be a physical column of the table; computed/metadata columns cannot participate in a key. validatePrimaryKey throws this ValidationException when the referenced column is a computed (non-physical) column.","triggerScenarios":"PRIMARY KEY clause naming a computed column (AS expr) or a metadata column (column METADATA FROM ...), e.g. PRIMARY KEY (event_ts_computed) NOT ENFORCED where event_ts_computed is defined with AS.","commonSituations":"Users trying to key tables on derived/computed values; DDL copied from sources with virtual columns; Flink SQL with generated columns.","solutions":["Make the key column a regular physical column (materialize the value on write).","Create a physical column that stores the computed expression's value and key on that instead.","Remove the computed column from the primary key."],"exampleFix":"// before\nts AS PROCTIME(), PRIMARY KEY (ts) NOT ENFORCED\n// after\nts TIMESTAMP(3), PRIMARY KEY (ts) NOT ENFORCED","handlingStrategy":"validation","validationCode":"for (String col : primaryKey.getColumns()) {\n  Column c = schema.getColumn(col);\n  if (c != null && !c.isPhysical()) {\n    throw new IllegalArgumentException(\"PK column must be physical: \" + col);\n  }\n}","typeGuard":"null","tryCatchPattern":"try {\n  FlinkSchemaUtil.toResolvedSchema(schema, partitionKeys, primaryKey);\n} catch (ValidationException e) {\n  LOG.error(\"PK on non-physical column: {}\", e.getMessage());\n  throw e;\n}","preventionTips":["Only reference physical columns in PRIMARY KEY clauses.","Materialize needed computed values into physical columns before keying on them.","Review DDL for AS/METADATA columns when defining keys."],"tags":["flink","schema","primary-key"],"backgroundTag":"schema-validation-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}