{"record":{"id":"5b801c660cd8f7fd","repo":"prestodb/presto","slug":"column-not-found","errorCode":"COLUMN_NOT_FOUND","errorMessage":"Column '%s' does not exist as a top-level column. The AFTER clause requires a top-level column name, not a nested field path.","messagePattern":"Column '(.+?)' does not exist as a top-level column\\. The AFTER clause requires a top-level column name, not a nested field path\\.","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergAbstractMetadata.java","lineNumber":1338,"sourceCode":"        }\n        if (position instanceof ColumnPosition.After) {\n            String afterColumnName = ((ColumnPosition.After) position).getColumnName();\n            // The engine lowercases the target name, while an Iceberg field keeps whatever case the table was\n            // created with, so the lookup has to be case-insensitive to match the name SHOW COLUMNS reports.\n            // Only the top-level columns are scanned, rather than using Schema.caseInsensitiveFindField: that\n            // builds a lower-case index over the whole schema, which resolves dotted paths to nested fields\n            // whose leaf name would be wrong here, and throws outright if any two fields anywhere in the table\n            // differ only by case.\n            //\n            // A name that does not resolve is rejected here rather than passed to moveAfter, which would raise\n            // an IllegalArgumentException for a name it cannot find. The engine already rejects a target that\n            // is not a real column, so this is the guard for a caller that goes through the SPI directly.\n            //\n            // A dotted name (e.g. \"struct_col.nested\") is a valid nested-field path in Iceberg but not a valid\n            // AFTER target: the AFTER clause positions a column among its siblings at the same nesting level,\n            // so only a top-level column name is accepted here.\n            if (afterColumnName.contains(\".\")) {\n                throw new PrestoException(COLUMN_NOT_FOUND, format(\n                        \"Column '%s' does not exist as a top-level column. The AFTER clause requires a top-level column name, not a nested field path.\",\n                        afterColumnName));\n            }\n            NestedField afterColumn = findTopLevelColumn(schema, afterColumnName)\n                    .orElseThrow(() -> new PrestoException(COLUMN_NOT_FOUND, format(\"Column '%s' does not exist\", afterColumnName)));\n            updateSchema.moveAfter(columnName, afterColumn.name());\n            return;\n        }\n        throw new PrestoException(NOT_SUPPORTED, \"Unsupported column position: \" + position);\n    }\n\n    /**\n     * Finds a top-level column of {@code schema} by name, preferring an exact match over a case-insensitive\n     * one, so that a table whose top-level columns differ only by case resolves deterministically rather than\n     * by whatever order the fields happen to be in.\n     */\n    private static Optional<NestedField> findTopLevelColumn(Schema schema, String columnName)\n    {","sourceCodeStart":1320,"sourceCodeEnd":1356,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergAbstractMetadata.java#L1320-L1356","documentation":"The AFTER clause of addColumn positions a new column among its siblings at the same nesting level, so the AFTER target must be a top-level column name. A dotted path like 'struct_col.nested' is a valid Iceberg nested-field path but not a valid AFTER target, so the connector raises COLUMN_NOT_FOUND with an explanatory message guarding direct SPI callers.","triggerScenarios":"Calling addColumn with a ColumnPosition.First(false, \"struct_col.nested\")-style after-column containing a '.'; the SQL parser normally rejects this, but direct SPI/procedure callers pass the dotted name through.","commonSituations":"Programmatic schema-evolution via the connector SPI; tools constructing ColumnPosition objects with nested field paths; users attempting to insert a nested field relative to a sibling inside a struct.","solutions":["Use only a top-level column name as the AFTER target.","To add a nested field inside a struct, add it to the struct's row type (modify the parent column's type) instead of using AFTER with a dotted path.","Validate that afterColumnName contains no '.' before calling addColumn."],"exampleFix":"// before\nmetadata.addColumn(session, handle, col, ColumnPosition.first(false, \"location.city\"));\n// after\nmetadata.addColumn(session, handle, col, ColumnPosition.first(false, \"location\"));","handlingStrategy":"validation","validationCode":"if (afterColumnName != null && afterColumnName.contains(\".\")) {\n    throw new IllegalArgumentException(\"AFTER target must be a top-level column, got: \" + afterColumnName);\n}\nmetadata.addColumn(session, handle, column, ColumnPosition.first(false, afterColumnName));","typeGuard":"boolean isTopLevelAfterTarget(ColumnPosition pos) {\n    return pos == null || !(pos instanceof ColumnPosition.First) || !((ColumnPosition.First) pos).getColumn().contains(\".\");\n}","tryCatchPattern":"try {\n    metadata.addColumn(session, handle, column, position);\n} catch (PrestoException e) {\n    if (e.getErrorCode().code() == COLUMN_NOT_FOUND && afterColumnName.contains(\".\")) {\n        throw new IllegalArgumentException(\"Use a top-level AFTER target, not nested path: \" + afterColumnName, e);\n    }\n    throw e;\n}","preventionTips":["Only pass simple top-level names to AFTER positions.","Add nested fields by modifying the parent struct type, not via AFTER.","Validate position targets against SHOW COLUMNS before schema evolution calls."],"tags":["iceberg","column-not-found","nested-field","column-position"],"backgroundTag":"column-not-found","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}