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
- DROP VIEW and re-CREATE VIEW (or CREATE OR REPLACE VIEW) so the stored column list matches the new underlying schema
- Check what changed: compare SHOW COLUMNS FROM view output against the underlying table's current columns
- Pin the view to explicit column names instead of SELECT * to make breakage explicit and controlled
- 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
- Update dependent views in the same migration that alters base tables
- Avoid SELECT * in view definitions; name columns explicitly
- Validate schema compatibility in CI by running view queries after migrations
- Version-control view DDL and re-apply after any base-table schema change
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.