prestodb/presto · error · SemanticException

VIEW_IS_STALE

VIEW_IS_STALE

Error message

View '%s' is stale; it must be re-created

What it means

A stored view's declared column list must still match the fields its query currently produces. If underlying tables changed (columns dropped, renamed, reordered, or type-incompatibly altered), isViewStale detects a mismatch between the persisted view columns and the live descriptor fields, and Presto throws VIEW_IS_STALE telling the user to recreate the view.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:2669

            analysis.getViewDefinitionReferences().addViewDefinitionReference(name, view);

            Optional<Expression> savedViewAccessorWhereClause = analysis.getCurrentQuerySpecification()
                    .flatMap(QuerySpecification::getWhere);
            savedViewAccessorWhereClause.ifPresent(analysis::setViewAccessorWhereClause);

            Query query = parseView(view.getOriginalSql(), name, table);

            analysis.registerNamedQuery(table, query, true);
            analysis.registerTableForView(table);
            RelationType descriptor = analyzeView(query, name, view.getCatalog(), view.getSchema(), view.getOwner(), table);
            analysis.unregisterTableForView();

            if (savedViewAccessorWhereClause.isPresent()) {
                analysis.clearViewAccessorWhereClause();
            }

            if (isViewStale(view.getColumns(), descriptor.getVisibleFields())) {
                throw new SemanticException(VIEW_IS_STALE, table, "View '%s' is stale; it must be re-created", name);
            }

            // Derive the type of the view from the stored definition, not from the analysis of the underlying query.
            // This is needed in case the underlying table(s) changed and the query in the view now produces types that
            // are implicitly coercible to the declared view types.
            List<Field> outputFields = view.getColumns().stream()
                    .map(column -> Field.newQualified(
                            table.getLocation(),
                            table.getName(),
                            Optional.of(column.getName()),
                            column.getType(),
                            false,
                            Optional.of(name),
                            Optional.of(column.getName()),
                            false))
                    .collect(toImmutableList());

            // Propagate source columns from the view's underlying query to the view's output fields.

View on GitHub (pinned to 55bb57d202)

Solutions

  1. DROP VIEW and re-CREATE VIEW (or CREATE OR REPLACE VIEW) so the stored column list matches the new underlying schema
  2. Check what changed: compare SHOW COLUMNS FROM view output against the underlying table's current columns
  3. Pin the view to explicit column names instead of SELECT * to make breakage explicit and controlled
  4. Coordinate schema migrations: update dependent views in the same migration as the base table change

Example fix

// before
-- underlying table dropped column 'extra'; view still declares it
CREATE VIEW my_view AS SELECT a, extra FROM base;
// after
DROP VIEW my_view;
CREATE VIEW my_view AS SELECT a FROM base;
Defensive patterns

Strategy: validation

Validate before calling

-- compare view columns against current base-table columns before querying
DESCRIBE my_view;
DESCRIBE base_table; -- field sets must match the stored view definition

Try / catch

try {
    return session.execute("SELECT * FROM my_view");
} catch (SemanticException e) {
    if (e.getCode() == VIEW_IS_STALE) {
        // recreate the view: DROP VIEW my_view; CREATE VIEW my_view AS ...
    }
    throw e;
}

Prevention

When it happens

Trigger: SELECT * FROM stale_view after an underlying table lost or renamed a column, or produced a different visible field set than recorded in the ViewDefinition. Detected in view analysis by comparing view.getColumns() with descriptor.getVisibleFields().

Common situations: ALTER TABLE ... DROP/RENAME COLUMN on a table feeding a view; schema evolution in Hive/Iceberg/Delta after view creation; restoring a table from backup with an older schema; cross-cluster replication lag changing column sets.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/6657717248a7cce7. Report an issue: GitHub.