TL;DR —
GoTo labeljumps execution to a line markedlabel:in the same procedure, with no condition attached. It has exactly one place it still belongs in modern VBA:On Error GoTofor error handling. For everything else — loops, skipping, branching — a jump is harder to follow than the structured tools (If,Exit For,Exit Sub,Do), and jumping into or out of a loop quietly corrupts the loop. Learn the syntax so you can read old code; reach for the structured version when you write new code.
Sub GoToBasics()
Dim x As Long
x = 5
If x > 0 Then GoTo Positive
Debug.Print "zero or negative"
Exit Sub ' stop before falling into the label below
Positive: ' a label - name followed by a colon
Debug.Print "positive"
End Sub
GoTo is the oldest control-flow tool there is: it moves execution to a named label, unconditionally and
in one direction of your choosing. VBA still supports it, and one modern feature — On Error GoTo — is
built on it. But for ordinary flow, a jump makes code you cannot read top to bottom, and that is why
almost every GoTo in new code should be an Exit or an If instead.
What you'll learn
- The label-and-jump syntax: how you write a label and how
GoToreaches it - The one place
GoTogenuinely belongs today —On Error GoToerror handling - Why jumping into or out of a loop corrupts the loop counter and leaves work half-done
- How
GoTogets misused to fake aContinueVBA does not have, and the clean alternative - Why you cannot
GoToa label in another procedure - The structured replacements that read better than any jump
The mental model: an unconditional one-way jump
A label is a name followed by a colon, alone on a line; GoTo label moves execution straight to it:
GoTo CleanUp ' jump forward (or backward) to the line marked CleanUp:
' ... any lines here are skipped ...
CleanUp: ' <-- execution resumes here
Two properties make GoTo powerful and dangerous in equal measure. It is unconditional — on its own
it always jumps, so branching requires wrapping it in an If — and it is a one-way move that leaves
no trail: nothing marks where control came from, so a reader tracing the code has to scan the whole
procedure to find every GoTo that could land on a label. A structured statement like Exit For tells
you exactly where control goes (just past Next); a GoTo can come from anywhere. That difference is
the entire case against it.
The one place GoTo belongs: On Error GoTo
Here is the exception, and it is a real one. VBA's error handling is built on GoTo: On Error GoTo
tells VBA which label to jump to when a run-time error occurs. This is idiomatic, expected, and has no
structured alternative in the language:
Sub SafeOpen()
On Error GoTo Failed
Workbooks.Open "C:\reports\data.xlsx"
Exit Sub
Failed:
MsgBox "Could not open the file."
End Sub
Notice the Exit Sub before the Failed: label — without it, the normal path would fall through into
the error handler even when nothing went wrong. That fall-through is the classic GoTo-labelled-code
bug, and it applies to every label, not just error handlers. For the full mechanism, see
VBA On Error. Outside of On Error, a GoTo is a warning sign, not a tool.
The trap: jumping into or out of a loop
The worst thing you can do with GoTo is jump across the boundary of a For or Do loop. Jump out
of a loop with GoTo and you bypass the loop's normal exit — the counter is left at whatever value it
held, and anything the loop was meant to finish is skipped. Jump into the middle of a loop and the
result is worse: the counter was never initialised, so behaviour is undefined:
' DO NOT do this - jumping out of a loop with GoTo
For i = 1 To 100
If Cells(i, 1).Value = "X" Then GoTo AfterLoop ' skips the loop's clean exit
Next i
AfterLoop:
The line above happens to run, but the honest version is Exit For, which leaves the loop through its
proper door and makes the intent obvious. Every "jump out of a loop" is better written as
Exit For or Exit Do; every "leave the procedure now" is better written as
Exit Sub. Those name the structure they leave and land control somewhere predictable — a GoTo does
neither.
Faking Continue with GoTo — and the clean alternative
VBA has no Continue statement to skip to the next loop iteration, so old code often simulates it by
placing a label just before Next and jumping to it:
' the GoTo way to "continue" - works, but it is a jump
For i = 1 To n
If Cells(i, 1).Value = "" Then GoTo NextRow
' ... process the row ...
NextRow:
Next i
This runs, but it asks the reader to jump around to understand a loop. The structured version simply
inverts the test so the skipped iterations do nothing and fall through to Next:
' the structured way - no label, no jump
For i = 1 To n
If Cells(i, 1).Value <> "" Then
' ... process the row ...
End If
Next i
Same behaviour, read straight down. When the skip condition is a single test, the If always wins. Save
the label-before-Next pattern for the rare case where the skip logic is too involved for one If —
and even then, consider splitting the body into its own procedure.
You cannot GoTo across procedures
A label is local to the procedure it lives in. GoTo can only jump to a label in the same Sub or
Function; you cannot jump from one procedure into another.
VBA raises the compile error Label not defined. If you find yourself wanting to jump into another
procedure, what you actually want is to call it — MyCleanup or Call MyCleanup — which returns
control to you afterward, exactly what a jump cannot do. This limitation is a feature: it keeps every
jump inside one readable unit.
When a jump is genuinely the least-bad option
Honesty demands one concession: breaking out of deeply nested loops is the one control-flow situation
where a GoTo to a label past the outer loop can be cleaner than the alternatives, because VBA cannot
Exit more than one loop level at a time. Even here, extracting the nested loops into a Function and
using Exit Function is usually clearer. The rule of thumb: if you are reaching for GoTo and it is not
On Error GoTo, stop and ask whether an Exit, an If, or a small helper procedure would say the same
thing without a jump. Ninety-nine times out of a hundred, it will.
How ExcelMaster helps
GoTo is easy to write and hard to read, which is exactly the combination that produces macros nobody
can maintain six months later. The two mistakes that bite are the fall-through into a label when a
preceding Exit Sub is missing, and jumps across loop boundaries that leave counters and work in an
undefined state.
ExcelMaster writes flow you can read top to
bottom: Exit For where a loop should stop, guard clauses where a procedure should bail out, and
On Error GoTo only where it belongs — in error handling, with the Exit Sub that keeps the normal path
out of the handler. You keep the workbook and the code.
Frequently asked questions
What does GoTo do in VBA?
GoTo label transfers execution unconditionally to a line marked label: in the same procedure. A label
is a name followed by a colon on its own line. Because GoTo always jumps, branching requires wrapping
it in an If, as in If x < 0 Then GoTo Negative. Execution then continues from the label onward.
Is GoTo bad practice in VBA?
For ordinary control flow, yes — a jump is harder to follow than structured statements, and jumping into
or out of a loop can corrupt the loop counter. The important exception is On Error GoTo, which is the
idiomatic and expected way to route run-time errors to a handler. Learn GoTo to read old code; prefer
Exit, If, and Do loops when you write new code.
How do I write a label for GoTo?
Put a name followed by a colon on its own line, such as CleanUp:, and jump to it with GoTo CleanUp.
Labels are case-insensitive and local to the procedure. Always place an Exit Sub before a label that
sits at the end of a procedure, or the normal path will fall through into the labelled code even when no
jump was intended.
Can GoTo jump to another Sub or Function?
No. A GoTo can only reach a label in the same procedure; jumping into another Sub or Function is a
compile error. To run code in another procedure, call it by name — the call returns control to you when
it finishes, which is what you usually want and what a jump can never do.
How do I break out of a loop without GoTo?
Use Exit For for a For loop or Exit Do for a Do loop — both leave the loop through its proper
exit and continue at the line after it. To skip a single iteration rather than leave the loop, wrap the
body in an If so the skipped passes do nothing. See VBA Exit For for the full set.
Tested in
Tested in: Excel 365 (Windows 11), VBA 7.1 — last verified 2026-09-24.
Related guides: VBA Exit For · VBA End · VBA On Error · VBA For Loop · VBA If Then Else
