IsNull
Tests a value for a genuine null. It returns False for every input a page variable can hold, including the unset variable the official example says returns true.
Syntax
IsNull(value) → boolean
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
value |
string | number | boolean | date | Yes | Value to test — typically a data extension field value retrieved with Lookup |
Example
The intended use is a data extension field that may carry no value at all:
%%[
VAR @nickname
SET @nickname = Lookup("Preferences", "Nickname", "SubscriberKey", _subscriberkey)
]%%
%%=IIf(IsNull(@nickname), "no nickname on file", @nickname)=%%
Reaching for it anywhere else silently takes the false branch. On a CloudPage this renders present even though nothing was ever assigned:
%%[ VAR @maybe ]%%
%%=IIf(IsNull(@maybe), "missing", "present")=%%
Use Empty for that check — for the same variable it renders True.
Return value
boolean — False when the value is not null.
Only the False literal was observed. Every one of the sixteen inputs probed rendered it, and the true token was never produced — so no set of return literals is claimed here, even though the sibling Empty proved the engine renders True readily for a boolean-returning Utility function.
Behaviour
Nothing a page variable can hold is null. An undeclared variable, a variable declared but never assigned, the empty string, three spaces, 0, "0", "false", an ordinary string and a date value each returned False.
Missing context is not null either. An attribute that does not exist, a query-string parameter that was not supplied, _subscriberkey, firstname, _messagecontext and jobid on an anonymous page request all returned False — as did an unset variable routed through v() and through Concat(), in case the accessor rather than the value decided the answer.
A non-string argument is accepted. IsNull(0) and IsNull(Now()) both rendered at HTTP 200 rather than aborting.
Only use-case: the function answers a question about data extension field values, and reports false everywhere else.
The unset variable the official example promises
The official reference’s own usage example declares a variable with VAR, never assigns it, and states the result is true. That exact shape returns False, at HTTP 200, with the page running to completion rather than aborting. See the differs-from-docs card.
How the four Utility tests compare
The same inputs compared across all four functions:
| Input | Empty |
IsNull |
IsNullDefault(x, "DEF") |
IIf(x, "T", "F") |
|---|---|---|---|---|
| undeclared variable | True | False | (empty) | F |
| declared, never set | True | False | (empty) | F |
"" |
True | False | (empty) | F |
" " |
False | False | ` ` | F |
0 |
False | False | 0 |
F |
"0" |
False | False | 0 |
F |
"false" |
False | False | false |
F |
"hello" |
False | False | hello |
F |
The IsNull column is constant. That is the practical summary: for a missing-value test on a page, this is the wrong function.
Show test script
%%[
VAR @b, @unset, @es, @sp, @z, @z0, @sf, @val
SET @b = RequestParameter("b")
SET @es = ""
SET @sp = " "
SET @z = 0
SET @z0 = "0"
SET @sf = "false"
SET @val = "hello"
/* known-good control: renders on every request, so a run of HTTP 422s
can be told apart from a broken deploy */
OutputLine(Concat("CTRL=[", Uppercase("ok"), "]"))
/* a name that was never declared */
IF @b == "n_und" THEN
OutputLine(Concat("--- n_und start ---"))
OutputLine(Concat("N_UND=[", IsNull(@zzz_never_declared), "]"))
OutputLine(Concat("--- n_und done ---"))
ENDIF
/* declared with VAR, never assigned - the shape the official example uses */
IF @b == "n_unset" THEN
OutputLine(Concat("--- n_unset start ---"))
OutputLine(Concat("N_UNSET=[", IsNull(@unset), "]"))
OutputLine(Concat("--- n_unset done ---"))
ENDIF
/* the explicitly empty string - the documented difference from Empty */
IF @b == "n_es" THEN
OutputLine(Concat("--- n_es start ---"))
OutputLine(Concat("N_ES=[", IsNull(@es), "]"))
OutputLine(Concat("E_ES=[", Empty(@es), "]"))
OutputLine(Concat("--- n_es done ---"))
ENDIF
/* three spaces */
IF @b == "n_sp" THEN
OutputLine(Concat("--- n_sp start ---"))
OutputLine(Concat("N_SP=[", IsNull(@sp), "]"))
OutputLine(Concat("--- n_sp done ---"))
ENDIF
/* the number zero, as a variable and inline */
IF @b == "n_z" THEN
OutputLine(Concat("--- n_z start ---"))
OutputLine(Concat("N_Z=[", IsNull(@z), "]"))
OutputLine(Concat("N_ZLIT=[", IsNull(0), "]"))
OutputLine(Concat("--- n_z done ---"))
ENDIF
/* the string "0" */
IF @b == "n_z0" THEN
OutputLine(Concat("--- n_z0 start ---"))
OutputLine(Concat("N_Z0=[", IsNull(@z0), "]"))
OutputLine(Concat("--- n_z0 done ---"))
ENDIF
/* the string "false" */
IF @b == "n_sf" THEN
OutputLine(Concat("--- n_sf start ---"))
OutputLine(Concat("N_SF=[", IsNull(@sf), "]"))
OutputLine(Concat("--- n_sf done ---"))
ENDIF
/* a genuine value, as a variable and inline */
IF @b == "n_val" THEN
OutputLine(Concat("--- n_val start ---"))
OutputLine(Concat("N_VAL=[", IsNull(@val), "]"))
OutputLine(Concat("N_VLIT=[", IsNull("hello"), "]"))
OutputLine(Concat("--- n_val done ---"))
ENDIF
/* a date value, to show a non-string argument is accepted */
IF @b == "n_date" THEN
OutputLine(Concat("--- n_date start ---"))
OutputLine(Concat("N_DATE=[", IsNull(Now()), "]"))
OutputLine(Concat("--- n_date done ---"))
ENDIF
/* an attribute that does not exist */
IF @b == "a_attr" THEN
OutputLine(Concat("--- a_attr start ---"))
OutputLine(Concat("N_ATTR=[", IsNull(AttributeValue("zzNoSuchAttribute")), "]"))
OutputLine(Concat("E_ATTR=[", Empty(AttributeValue("zzNoSuchAttribute")), "]"))
OutputLine(Concat("--- a_attr done ---"))
ENDIF
/* a query-string parameter that was not supplied */
IF @b == "a_rp" THEN
OutputLine(Concat("--- a_rp start ---"))
OutputLine(Concat("N_RP=[", IsNull(RequestParameter("zzNoSuchParam")), "]"))
OutputLine(Concat("E_RP=[", Empty(RequestParameter("zzNoSuchParam")), "]"))
OutputLine(Concat("--- a_rp done ---"))
ENDIF
/* the subscriber key on an anonymous page request */
IF @b == "s_sub" THEN
OutputLine(Concat("--- s_sub start ---"))
OutputLine(Concat("N_SUB=[", IsNull(_subscriberkey), "]"))
OutputLine(Concat("E_SUB=[", Empty(_subscriberkey), "]"))
OutputLine(Concat("--- s_sub done ---"))
ENDIF
/* a profile attribute outside a send */
IF @b == "s_prof" THEN
OutputLine(Concat("--- s_prof start ---"))
OutputLine(Concat("N_PROF=[", IsNull(firstname), "]"))
OutputLine(Concat("E_PROF=[", Empty(firstname), "]"))
OutputLine(Concat("--- s_prof done ---"))
ENDIF
/* send-context tokens that only exist inside a job */
IF @b == "s_msgctx" THEN
OutputLine(Concat("--- s_msgctx start ---"))
OutputLine(Concat("N_MSG=[", IsNull(_messagecontext), "]"))
OutputLine(Concat("N_JOB=[", IsNull(jobid), "]"))
OutputLine(Concat("--- s_msgctx done ---"))
ENDIF
/* the unset variable routed through two different accessors, in case the
accessor rather than the value decides the answer */
IF @b == "s_var" THEN
OutputLine(Concat("--- s_var start ---"))
OutputLine(Concat("N_V=[", IsNull(v(@unset)), "]"))
OutputLine(Concat("N_CONCAT=[", IsNull(Concat(@unset)), "]"))
OutputLine(Concat("--- s_var done ---"))
ENDIF
]%%
Proving the true literal needs a data extension field holding a genuine database null. Two attempts to build one — inserting a row with unset columns, then a bare lookup against the same data extension — both aborted the page at compile time, including on a plain request with no branch selected. Nothing from those deploys was attributed to the function, and the check remains open rather than guessed at.
Availability
| Platform | Available |
|---|---|
| Marketing Cloud Engagement | Yes |
| Marketing Cloud Next | Yes (since 67) |
See also
- Empty — what to use instead for a missing-value test; it separates the inputs this function cannot
- IsNullDefault — the same null definition with a fallback value bolted on
- IIf — what usually consumes the boolean
- The unset variable is not reported as null
- Official reference
- ampscript.guide