apache/iceberg · error

recursiveViewDetectedError(viewIdent, newCyclePath)

Error message

recursiveViewDetectedError(viewIdent, newCyclePath)

What it means

Iceberg's CheckViews detects cyclic view references during analysis: checkCyclicViewReference walks the analyzed plan tracking the chain of view identifiers, and if a view resolves back to itself, recursiveViewDetectedError is thrown with the full cycle path. This prevents infinite recursion and stack overflows when a view references itself directly or transitively.

Source

Thrown at spark/v4.2/spark-extensions/src/main/scala/org/apache/spark/sql/catalyst/analysis/CheckViews.scala:118

        plan.children.foreach(child => checkCyclicViewReference(viewIdent, child, cyclePath))
    }

    plan.expressions.flatMap(_.flatMap {
      case e: SubqueryExpression =>
        checkCyclicViewReference(viewIdent, e.plan, cyclePath)
        None
      case _ => None
    })
  }

  private def checkIfRecursiveView(
      viewIdent: Seq[String],
      currentViewIdent: Seq[String],
      cyclePath: Seq[Seq[String]],
      children: Seq[LogicalPlan]): Unit = {
    val newCyclePath = cyclePath :+ currentViewIdent
    if (currentViewIdent == viewIdent) {
      throw QueryCompilationErrors.recursiveViewDetectedError(viewIdent, newCyclePath)
    } else {
      children.foreach { c =>
        checkCyclicViewReference(viewIdent, c, newCyclePath)
      }
    }
  }
}

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Break the cycle: reference the underlying table instead of the view itself in the view definition
  2. Use a different name for the new view if the old definition was intended as the data source
  3. Restructure the view chain (v1 -> v2 -> v1) so references form a DAG
  4. Read the cycle path in the error to find which two views form the loop and fix one side

Example fix

-- before (recursive)
CREATE OR REPLACE VIEW v AS SELECT * FROM v;
-- after
CREATE OR REPLACE VIEW v AS SELECT * FROM base_table;
Defensive patterns

Strategy: validation

Validate before calling

import org.apache.spark.sql.catalyst.analysis.CheckAnalysis
// inspect view definition for self-reference before replacing
val body = spark.sql("SELECT * FROM v").queryExecution.analyzed
val selfRef = body.collectLeaves().exists(_.name == "v")
require(!selfRef, "view definition references itself")

Prevention

When it happens

Trigger: `CREATE OR REPLACE VIEW v AS SELECT * FROM v` or a cycle like v1 -> v2 -> v1; the error fires in CheckViews when currentViewIdent equals the outer viewIdent being resolved, with newCyclePath showing the reference chain.

Common situations: CREATE OR REPLACE VIEW whose new definition still references the old view of the same name; accidentally reusing the view name in its replacement query; generated SQL templating a self-reference.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/f8e1d3177e65b536. Report an issue: GitHub.