VSTACK concatenates its array arguments vertically, in argument order.
Given arrays A₁ … A_p with dimensions m₁ × n₁ … m_p × n_p, the result has
rowsm₁ + m₂ + … + m_p — the sum,
columnsmax(n₁, …, n_p) — the maximum,
with A_k's rows appearing in order, starting at result row m₁ + … + m_{k-1} + 1.
The interesting part is the second dimension. Stacking arrays of unequal width is not an error
and not a truncation: the narrow arrays are padded on the right with #N/A. That choice is
worth pausing on. Excel could have refused, or padded with blanks, or padded with zeros. It
pads with an error value, which means the padding is self-announcing — it shows up in the
result rather than blending into it, and it propagates through arithmetic instead of quietly
contributing 0.
The practical consequence is the idiom Microsoft itself recommends: wrap the call in IFERROR
to substitute your own filler. That works, and it also silently swallows any genuine #N/A
that came from the data. Whether that trade is acceptable is the caller's decision, and it is
worth making consciously.
Arguments
There are no option flags. VSTACK has exactly one behaviour, which is unusual in this family
and makes it easy to reason about.
The position most often misunderstood is not an argument at all but the shape contract: many
readers expect VSTACK to require equal widths, discover that it does not, and only later find
the #N/A padding in the corner of a wide result. There is no argument that turns the padding
off.
Try it
Quirks
Result and edge cases
The return kind is an array, spilled.
Single argument.VSTACK(A) returns A. It is a legal, if pointless, call, and it is a
useful shape probe.
Scalars. A scalar argument behaves as a 1×1 array and contributes one row.
The #N/A padding is part of the value, not a display artifact: it participates in
downstream arithmetic and comparisons exactly as any #N/A does.
Error values inside the inputs are carried through unchanged. VSTACK moves values; it
does not inspect them. An error written directly as an argument is different: it is the answer
for the whole call (FN-HSTACK-001).
Very large results hit the grid and the array-size limits; the documentation for adjacent
reshaping functions names #NUM! for oversized arrays, and whether VSTACK uses the same
outcome is not something the Handbook has verified.
Dynamic-array publication and #SPILL! are host-side adaptation, described in
the call pipeline.
Errors
The #N/A values in the padded region are documented as the expected result of stacking
unequal widths, not as a failure — an important distinction for anyone writing a test oracle:
a result containing #N/A is not necessarily an error case.
Microsoft's page documents no error return for VSTACK itself beyond that padding, and
suggests IFERROR as the mitigation. #SPILL! arises at publication when the result cannot be
placed.
A direct error stops these array functions; the same error in an array or cell is an element
An error written directly as an argument of these functions is the answer for the whole call. It is not placed in the result: HSTACK(1,#VALUE!) is #VALUE!, not a row holding 1 and an error, and TAKE(#N/A,0) is #N/A. With MAP the LAMBDA does not run at all: MAP(NA(),LAMBDA(xx,IFERROR(xx,3))) is #N/A. The same error inside an array, or in a referenced cell, is an ordinary element: MAP({#N/A},LAMBDA(xx,3)) is 3, and HSTACK(Data!C1,{1;2}) with #N/A in Data!C1 is a 2 by 2 array with #N/A in its first column. TOCOL and TOROW show the difference with ignore 2: TOCOL(#N/A,2) is #N/A, while TOCOL({#N/A},2) drops the error, has nothing left, and is #CALC!. EXPAND is an exception on the cell side: EXPAND(Data!C1,2,2,"pad") is #N/A for the whole call. BYROW and BYCOL check the direct error first too: BYROW(#N/A,#VALUE!) is #N/A, while BYROW({#N/A},2), where 2 is not a LAMBDA, is #VALUE!.
Formula
Excel
Mathematical value
=HSTACK(1,#VALUE!) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#VALUE!
—
=TAKE(#N/A,0) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=MAP(NA(),LAMBDA(xx,IFERROR(xx,3))) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=MAP({#N/A},LAMBDA(xx,3)) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
3 0x4008000000000000
—
=HSTACK(Data!C1,{1;2}) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Number supplied through a cell.
Data!C1: #N/A
{#N/A,1;#N/A,2}
—
=TOCOL(#N/A,2) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=TOCOL({#N/A},2) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#CALC!
—
=EXPAND(Data!C1,2,2,"pad") Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Number supplied through a cell.
Data!C1: #N/A
#N/A
—
=BYROW(#N/A,#VALUE!) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Typed formula.
#N/A
—
=BYROW({#N/A},2) Excel 16.0.20527.20088 Current Channel, Windows 64-bit (NT 10.0.26200), x86-64, AMD Ryzen 7 4800H, en-ZAInputs and reference value
Compute the target width first, then copy; the padding must be materialized, not implied,
because the result is a dense rectangular array.
The pad value is #N/A specifically, not a generic error and not a blank. An implementation
that pads with empty produces a result that looks right and sums differently.
Argument order defines row order. Stacking is not commutative.
The result of stacking a large number of arguments is the natural place for an array-size
limit check; the limit is a host property, not a kernel property.
Rust code
Show the CalcFunc source of this function
Quoted from the public repository DnaCalc/DnaCalc, crates calc-func and calc-fml, at commit 9e1884369a67. Comments are the source's own. A kernel shared with other functions appears once, on its module page.
10 vstack_fn "VSTACK" (1, 254) [R"array1", R"array2"] |s, a, sv| {
// Fn2: a direct error in any source precedes stacking; reference/array cells remain cells.for source in a { scalar_error(source)?; }
run(s, a, sv, k::eval_vstack_prepared)
}
No implementation has been published with a Handbook test suite for this function.
Version differences
No comparison between specific Excel builds is recorded here yet. This does not establish that all builds behave alike.
Alternatives
Relationships
HSTACK is the horizontal twin: columns sum, rows take the maximum, and short arrays are
padded below with #N/A. The two are exact transposes of each other, and
TRANSPOSE(VSTACK(TRANSPOSE(a), TRANSPOSE(b))) is HSTACK(a, b).
TOCOL / TOROW flatten an array into one vector; VSTACK preserves two-dimensional
structure. Stacking single-column arrays with VSTACK and flattening with TOCOL produce
different things whenever the inputs are wider than one column.
WRAPROWS / WRAPCOLS are the inverse direction: one vector to a rectangle.
EXPAND pads a single array to a given size with a chosen filler, which is the explicit
form of what VSTACK does implicitly.
The array literal {1,2;3,4} and the ; row separator are the static equivalents.