Runtime verified Differs from official docs Test scripts included

Syntax

IsNull(value)  →  boolean
1 argument — exactly

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

booleanFalse 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.

The community guide’s framing matches what was observed: 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 returned False. The gate printed its own start and done markers at HTTP 200 alongside a known-good control block, so the page ran to completion — this is a result, not a swallowed abort. See the differs-from-docs card.

How the four Utility tests compare

The same inputs put through all four functions, on one page, in one run:

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
]%%

One check could not be closed

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