Skip to content

#CYCLE!: a cycle of cell references

#CYCLE! means that a cell of a sheet reads itself, directly or through other cells. A spreadsheet shows a circular reference warning for the same problem.

Draw the reads, find the cycles
abcd
readsabcdifError
a
b
c
d
a1 + b#CYCLE!
b2 + a#CYCLE!
c3 + ifError(a, 0)3
d44

In the start state, the column a reads b, and b reads a. Both cells give #CYCLE!. The column c reads a, but a does not read c, so c is not on the cycle. Its ifError changes the error to 0.

Kind Cause
cycle The cell is on a cycle of cell references: a column reads its own value at the same key, directly or through other cells. A cycle can go across keys, for example a column that reads itself at other.

Each cell on a cycle gives #CYCLE!, also when its formula has ifError:

const loop = root
.sheet()
.column("a", (r) => r.start(r.cell<number>("b")).ifError(0)._.add(1))
.column("b", (r) => r.start(r.cell<number>("a")).ifError(0)._.add(1));
loop.result("A", "a"); // => { ok: false, error: { code: "#CYCLE!", ... } }
loop.result("B", "b"); // => { ok: false, error: { code: "#CYCLE!", ... } }

If ifError could hide a cycle, the first cell that the sheet evaluates gets the error, and the other cell gets a value. The results then depend on the order of the evaluations. Vex finds each cycle with the algorithm of Tarjan for strongly connected components, so the result is the same in each order.

A Vex expression is a tree of plain data. A let binding evaluates with the names from outside the let, so a binding cannot read its own name. Only the cell references of a sheet can make a cycle.

  • Remove one reference of the cycle. The page sheets and cycles shows each read.
  • For a recurrence, read a different key: a running total reads the row above with offset(-1), not its own row.

Refer to error values for the other codes.