How to Inspect Variables, Watch Expressions, and the Call Stack in VS Code

Inspect local and global variables, set watch expressions that update as you step, and read the call stack to trace how your code reached the current line.

7 min read

When the debugger pauses at a breakpoint, your job is to figure out what is happening. VS Code gives you three tools for this: the VARIABLES section, the WATCH section, and the CALL STACK.

All three live in the Run and Debug view and update every time you step. If you have not started a debug session before, see the beginner's guide to Run and Debug first.

Inspect variables in the VARIABLES section

The VARIABLES section is the first pane in the Run and Debug sidebar. It shows every variable that is in scope at the current paused line.

Variables are organized into groups:

  • Local: Variables declared inside the current function, including function parameters.
  • Global: Variables in the global scope of your program.
  • Closure: Variables captured by closures, visible when paused inside a function defined within another function.

Each variable shows its name and current value. Objects and arrays are expandable: click the arrow next to an object to see its properties, or next to an array to see each element. Nested objects expand further so you can drill into deeply nested data.

Large objects show only the first few properties by default. Expand nodes as needed instead of waiting for the full tree to render.

Filter variables

When you have many variables, use the filter to narrow them down. With focus in the VARIABLES section, press Ctrl+Alt+F (Windows/Linux) or Cmd+Opt+F (macOS) and type a search term. Only matching variables appear.

Change a variable value

You can modify a variable's value during a debug session. Right-click the variable in the VARIABLES section and select Set Value. You can also select the variable and press F2 on Windows/Linux or Enter on macOS.

Type the new value and press Enter. The change applies immediately.

This is useful for testing edge cases. If a bug happens when a counter equals zero, set it to 0 while paused and continue execution. The change only lasts for the current session. Restarting the debugger resets everything.

Copy a variable or expression

Right-click a variable and select Copy Value to copy its current value as a string. Select Copy as Expression to copy a path expression that you can paste into the WATCH section or Debug Console. For example, it might copy something like user.address.city.

Hover to inspect values inline

You do not need to switch to the VARIABLES section every time. While paused at a breakpoint, hover over any variable name in the editor. A popup appears with the variable's current value.

The hover popup shows the same expandable tree as the VARIABLES section. Expand objects, drill into arrays, and inspect nested properties without leaving the editor.

This is the fastest way to check a value when you are stepping through code line by line. Step with F10, glance at the variables you care about via hover, and keep moving.

Set watch expressions in the WATCH section

The WATCH section lets you track specific expressions that recalculate after every debug step. Unlike the VARIABLES section, which shows every variable automatically, WATCH only shows what you explicitly add.

Click the + button in the WATCH section header and type any valid expression. Press Enter to add it. The expression evaluates in the context of the current stack frame and displays the result.

Examples of useful watch expressions:

  • myArray.length to track whether an array grows or shrinks.
  • A compound boolean like user.isActive && user.hasPermission.
  • A DOM query like document.querySelector(".error") during browser debugging.
  • response.status to see the HTTP status code after a fetch call.

Each watched expression re-evaluates after every step, breakpoint hit, or manual Continue. If an expression throws an error, the watch entry shows the error message instead of a value. This is useful for spotting when a variable becomes undefined or null.

Remove a watch expression by hovering over it and clicking the X, or select it and press Delete.

The CALL STACK section shows the chain of function calls that led to the current paused line. The current function is at the top. The function that called it is below. The entry point (main, global scope, or the initial event handler) is at the bottom.

Each line in the call stack shows the function name, the file path, and the line number.

Click any entry in the call stack to jump to that function's location. The editor opens the file and highlights the line where the call was made. The VARIABLES and WATCH sections update to show the values from that stack frame's scope.

This is critical for answering the question, "How did I get here?" If a variable has a bad value, walk up the call stack to find where the bad value originated. Click each frame, inspect the local variables, and trace the data backward.

Multi-target sessions

When debugging multiple processes at once (a client and a server, for example), the CALL STACK section shows each session as a top-level node. Expand the session to see its stack frames. The debug toolbar shows which session is currently active, and debug actions apply to that active session.

Use the Debug Console for one-off expressions

The Debug Console is the interactive counterpart to the WATCH section. While WATCH is for expressions you want to track continuously, the Debug Console is for expressions you want to evaluate once.

Open it with Ctrl+Shift+Y (Windows/Linux) or Cmd+Shift+Y (macOS). Type any expression and press Enter. The result prints on the next line. You can also call functions, test method return values, and assign variables.

For example, while paused in a function that processes a list, type items.filter(i => i.price > 100) to see which items match. Type user.email.toLowerCase() to confirm a string method behaves as expected. Type fetch("/api/health").then(r => r.status) to make a network call and see the result.

The Debug Console supports multi-line input. Press Shift+Enter to add a new line without sending the expression for evaluation. Press Enter on the last line to evaluate the entire block.

A debugging investigation workflow

Here is how these three sections work together during a real bug investigation:

Pause at a breakpoint

Execution stops. Look at the VARIABLES section to see the current state at a glance. Hover over suspicious values in the editor.

Add a watch expression

If you suspect a specific variable, add it to WATCH so you can see it change as you step.

Walk the call stack

If the value is already wrong, click up the CALL STACK to find where it was set. Inspect variables at each frame.

Test a theory in the Debug Console

Type an expression that tests your hypothesis. If you think an API call failed, type response.status in the console.

Change a value and continue

If you want to test a fix, right-click the bad variable, select Set Value, and type the corrected value. Press F5 to see if the rest of the code behaves.

This cycle of pause, inspect, trace, test, and modify turns debugging from guesswork into a systematic process.

Troubleshooting

A variable I know exists is not in the VARIABLES list

The variable might be out of scope at the current paused line. Click up the call stack to a frame where the variable was in scope. If it is declared in a parent function and you are paused in a nested function, check the Closure section.

My watch expression shows an error

The expression might reference a variable that is not in scope at the current stack frame, or it might use syntax from the wrong language. Click a different call stack frame and the expression may resolve. Double-check that you used the correct language syntax for the expression.

The Debug Console does not show autocomplete

The Debug Console uses the language mode of your active editor tab. If you have a Markdown or JSON file open, the console may not provide JavaScript or Python autocomplete. Switch to a code file of the target language before using the console.

I cannot expand a large object in VARIABLES

Very large objects can be slow to expand. Instead of expanding the full tree, use the Debug Console to query the specific property you need. Type myObject.deeply.nested.property and press Enter. The console fetches only that value.

Rune AI

Rune AI

Key Insights

  • The VARIABLES section shows every variable in scope, organized into Local, Global, and Closure categories. Values update as you step through code.
  • Hover over any variable in the editor to see its value in a popup without switching views.
  • Add an expression to the WATCH section by clicking the + button. Watched expressions recalculate after every debug step.
  • The CALL STACK shows every function that led to the current line. Click any frame to jump to that location and see its local variables.
  • Right-click a variable and select Set Value to change it during the session. The new value takes effect on the next line.
  • Use the Debug Console to evaluate expressions interactively. Type any valid expression and press Enter while paused.
RunePowered by Rune AI

Frequently Asked Questions

Why does the VARIABLES section show some values as dimmed?

Dimmed variables are not in the current scope or are not yet initialized. The VARIABLES section shows every variable declared in the current function, but only the ones that have been assigned a value show as bright. Variables declared later in the function appear dimmed until the execution passes their declaration line.

Can I change a variable's value while debugging?

Yes. In the VARIABLES section, right-click a variable and select Set Value, or select the variable and press F2 on Windows/Linux or Enter on macOS. Type the new value and press Enter. The change takes effect immediately. Use this to test how your code behaves with different data without restarting the debug session.

What is the difference between the Debug Console and the WATCH section?

The Debug Console evaluates one expression at a time and shows the result inline. The WATCH section evaluates expressions automatically after every step and shows the updated value. Use WATCH for expressions you want to track continuously. Use the Debug Console for one-off queries or to call methods.

How do I find where a function was called from?

Look at the CALL STACK section in the Run and Debug view. It lists every function call that led to the current line, from the entry point at the bottom to the current function at the top. Click any entry to jump to that function's code and see the VARIABLES section update to that frame's values.

Conclusion

The VARIABLES, WATCH, and CALL STACK sections in the Run and Debug view turn a paused breakpoint into a full investigation. Use VARIABLES to see the current state at a glance. Use WATCH to track expressions that matter to your bug. Use the CALL STACK to trace the path your code took to reach this point. Hover over values in the editor for a quick look, and use the Debug Console to run one-off expressions. Paired with breakpoints and logpoints, these tools give you everything you need to understand and fix bugs without restarting your program.