Errors

Errors And Diagnostics

Errors And Diagnostics

In Grid, errors are first-class typed values. They flow through formulas just like numbers and strings, and you handle them with explicit error-handling functions and operators.

This doc lists every error code, when it fires, what it means, and how to recover.


1. The Error Value

Every error has:

  • A code like VALUE, N/A, DIV/0, REF.
  • A message explaining the cause.
  • A typeTag of the form error:<code>.

In source code, 9 error literals are writable using the Excel-compatible spelling:

#N/A      #DIV/0!    #VALUE!    #REF!
#NAME?    #NULL!     #NUM!      #CALC!    #SPILL!

You can write any of these anywhere a value is expected:

A1 = #N/A
B1 = IF(missing, #N/A, value)

Two additional error codes are runtime-generated only — the parser rejects them as literals because they describe structural problems that source code shouldn't claim to produce:

Code Why no literal
#TYPE! Generated when a type-tag check fails on an assignment
#CIRC! Generated when the dependency graph contains a cycle

If you need to construct one of these in a formula, use a function:

A1 = IF(condition, errorValue("TYPE", "..."), normalValue)   # not currently exposed

In v1, #TYPE! and #CIRC! are values you read, not values you write.


2. Every Error Code

Code Literal Fires when Recover with
N/A #N/A A lookup found no match; a function explicitly signals "no value" IFNA, IFERROR, DEFAULT, ?=
DIV/0 #DIV/0! Division by zero IFERROR, TRY ... ELSE, defensive predicate
VALUE #VALUE! Wrong argument shape, incompatible kinds, parse failure inside a typed literal IFERROR, WITH ... ELSE
REF #REF! A reference is invalid or out of bounds; spill anchor is wrong; OFFSET / INDIRECT resolves to nothing Re-check addresses; structural fix
NAME #NAME? Unknown function name or unknown named argument Check spelling; check getFunctionDefinition
NULL #NULL! Intersection of ranges is empty Re-check range syntax
NUM #NUM! Numeric argument out of domain (e.g. SQRT(-1)); singular matrix; convergence failure Validate inputs; choose different algorithm
CALC #CALC! Iterative method failed (e.g. ROOT did not converge); empty FILTER result Tune tolerances; provide if_empty argument
SPILL #SPILL! An array-returning formula's spill rectangle hits a populated cell Move the formula or clear the obstructing cell
TYPE #TYPE! A typed assignment receives a value incompatible with its type tag Match the kind to the tag, or remove the tag
CIRC #CIRC! A cyclic dependency was detected during model build Break the cycle

Three additional internal codes you may see in API responses:

Code Source Meaning
TIMEOUT External worker The worker exceeded its timeoutMs budget
EXTERNAL External adapter The adapter raised an exception
STALE_FALLBACK Runtime A fresh value failed; the runtime returned the last cached value

3. Error Propagation

Errors short-circuit. A function that receives an error argument returns that error unchanged unless it is one of the explicit error-handling functions.

A1 = #N/A
A2 = A1 + 5            # → #N/A (propagated)
A3 = SUM(A1, 1, 2)     # → #N/A (propagated)
A4 = IFERROR(A1, 0)    # → 0 (handled)

The first error encountered in a positional argument list wins. Named arguments don't change this rule.


4. Error-Handling Functions

These functions are designed to consume errors:

Function Behavior
IFERROR(value, fallback) If value is any error, return fallback; else return value
IFNA(value, fallback) Like IFERROR but only catches #N/A
ISERROR(value) TRUE if value is any error
ISERR(value) TRUE if value is an error other than #N/A
ISNA(value) TRUE if value is #N/A
ERROR.TYPE(value) Numeric code matching Excel: 1=#NULL!, 2=#DIV/0!, 3=#VALUE!, 4=#REF!, 5=#NAME?, 6=#NUM!, 7=#N/A

5. Error-Handling Operators And Clauses

5.1 DEFAULT / ??

The cheapest fallback. Returns the right-hand side if the left is BLANK or any error.

A1 DEFAULT 0          # canonical
A1 ?? 0               # accepted sugar

5.2 TRY ... ELSE

Inline form for guarding a single expression:

TRY 10 / 0 ELSE 0     # → 0

5.3 WITH ... THEN ... ELSE

For multi-step external chains where any step might fail:

WITH data = HTTP_JSON(url), users = data.results
THEN users[1].name ELSE "unavailable"

If HTTP_JSON fails, or data.results errors, or the then expression errors, the ELSE value is returned.

The bindings list can span lines (each , at end-of-line), but the final THEN <expr> ELSE <fallback> must be on a single line.

5.4 ASSERT Clause

Short-circuit a value through a predicate:

value ASSERT value > 0                    # #VALUE! if assertion fails
value ASSERT value > 0 ELSE #N/A          # custom error
score ASSERT score BETWEEN 0 AND 1

5.5 ?= (Conditional Eager Assignment)

If the right-hand side is any error, skip the assignment — preserve the previous value:

A3 = 0
A3 ?= MAYBE_FAIL()    # if MAYBE_FAIL() errors, A3 stays at 0

6. Validation Errors (Build-Time, Not Runtime)

Some errors fire at build time (when the model is parsed or constructed), not at evaluation time. They surface as diagnostics in the API response, not as cell values.

Diagnostic Cause
Parse error at line N Source could not be parsed
Cyclic reference detected Same as #CIRC! but reported at build
Unknown function FOO The model references a function not in the registry
Range target spans more than X cells Configurable cell-count limit on range broadcasts
Mixed ownership: B1 has both top-level and rule-action assignments A target cannot be owned by both

A model with build-time diagnostics will not deploy. Fix all diagnostics first.


7. The Error Type Tag

Every error value carries typeTag = "error:<code>". You can match on it explicitly:

A1 IS ERROR           # TRUE for any error
ISERR(A1)             # TRUE for any error other than #N/A

Format / display:

TEXT(A1, "")          # error string e.g. "#DIV/0!"

8. Worked Examples

8.1 Defensive Division

A1 = numerator
A2 = denominator
A3 = IFERROR(A1 / A2, 0)              # → 0 if A2 is 0

Or:

A3 = TRY A1 / A2 ELSE 0

Or:

A3 = A2 = 0 THEN 0 ELSE A1 / A2       # explicit guard

8.2 Lookup With Fallback

A1 = VLOOKUP("Alice", users, 2, FALSE) DEFAULT "n/a"
A2 = IFNA(VLOOKUP("Bob", users, 2, FALSE), "n/a")

8.3 External Call With Fallback

B1 = FX_RATE("EUR", "USD")
B2 as currency = ROUND(amount * (B1 DEFAULT 1.08), 2)

While B1 is queued or failed, B2 uses the fallback rate.

8.4 Multi-Step Chain

result = WITH data = HTTP_JSON(url), first = data.results[1], name = first.name
THEN name ELSE "unknown"

8.5 Validating An Input

A1 as percentage = input ASSERT input BETWEEN 0 AND 1 ELSE #VALUE!

8.6 Detecting Specific Errors

status = IF(ISNA(A1), "missing",
         IF(ISERR(A1), "error",
         IF(ISBLANK(A1), "empty", "ok")))

9. Common Mistakes

9.1 Forgetting Errors Propagate

A1 = SUM(B1:B10)         # B5 = #N/A → A1 = #N/A

If B1:B10 may contain errors, filter first:

A1 = SUM(IFERROR(B1:B10, 0))

Or use AGGREGATE with the "ignore errors" mode:

A1 = AGGREGATE(9, 6, B1:B10)

9.2 Confusing Blank With Empty String

A1 = ""                 # empty string, not blank
A2 = BLANK              # blank
ISBLANK(A1)             # FALSE
ISBLANK(A2)             # TRUE

9.3 Catching #N/A With ISERROR

ISERROR(#N/A)           # TRUE — catches everything
ISERR(#N/A)             # FALSE — explicitly excludes #N/A
ISNA(#N/A)              # TRUE — catches only #N/A

Use ISERR if you want "errors but not missing-values".

9.4 Treating External Pending States As Errors

A cell whose external call is queued or running is not an error. It carries a previous cached value (or BLANK) with a stale status. Detect via the API status field, not by ISERROR.

9.5 Using ?= On A Target That Doesn't Exist Yet

A1 ?= MAYBE_FAIL()      # if MAYBE_FAIL fails, A1 has no previous value to preserve → A1 stays uninitialized (BLANK)

?= only protects against overwriting; it doesn't conjure a value.


10. See Also