TL;DR
Variables set during manual request execution often vanish when you run the same collection in Postman’s Collection Runner. The root cause is a variable scope mismatch: pm.environment.set behaves differently in the runner than in manual mode, and collection variables work differently from environment variables. This guide explains exactly why this happens and how to fix it, then shows how Apidog handles variable scope in a way that’s harder to accidentally misconfigure.
Introduction
You’ve been testing an API manually in Postman. You run a login request, the pre-request script sets an auth token, and subsequent requests pick it up fine. Everything works.
Then you click “Run Collection.” The runner starts, the login request succeeds, but the next request fails with a 401. The token isn’t there.
This scenario shows up repeatedly on Stack Overflow and the Postman community forums. The responses usually point to variable scope, but they rarely explain the full picture of why the behavior differs between manual testing and the runner.
Understanding this requires understanding Postman’s variable scope hierarchy. Once you see it clearly, the fix is straightforward.
Postman’s variable scope hierarchy
Postman has five variable scopes, listed from highest to lowest priority:
- Local variables – available only within the current script execution, not accessible across requests
- Data variables – loaded from a CSV or JSON file for data-driven runs
- Collection variables – scoped to the collection, persist across requests in that collection
- Environment variables – scoped to the selected environment, persist across collections
- Global variables – available in any collection in any environment
When Postman resolves a variable reference like {{token}}, it walks down this list and uses the first match it finds.
The problem is that “persist” means different things depending on how you’re running the collection.
Why variables vanish in the Collection Runner
The current value vs. initial value distinction
Every Postman variable has two fields: “initial value” and “current value.”
- Initial value is synced to Postman’s cloud and shared with teammates.
- Current value is local to your machine and never synced.
When you set a variable programmatically in a script using pm.environment.set('token', value), Postman updates the current value in your active environment.
In manual testing mode, this works as expected because your local current values persist across individual request executions within the same session.
In the Collection Runner, the behavior changes. The runner starts each run from the environment’s current state at run start. Scripts that update variables during the run work correctly within that run. But if the runner is configured to clear variables between runs, or if the initial value is empty and the runner uses a fresh environment snapshot, you can end up in a state where your script runs but the variable doesn’t appear to persist.
The environment variable vs. collection variable issue
Here’s the more common trap. You’re setting a variable in a post-response script:
pm.environment.set('token', pm.response.json().access_token);
This sets a variable in the active environment. But the Collection Runner has an option called “Keep variable values.” If this checkbox is unchecked (it defaults to unchecked), the runner resets environment variables to their initial values after the run. Any value set during the run is discarded.
If you’re running the collection without an environment selected, pm.environment.set fails silently. There’s no environment to write to. The variable disappears.
The scope mismatch between manual and runner mode
In manual testing, you typically have an environment selected and it persists across your entire Postman session. When you run a request, set a variable, and run another request, they’re all happening in the same persistent environment context.
The Collection Runner creates a separate execution context. It loads the environment state at the start, runs all requests, and then (unless you check “Keep variable values”) returns the environment to its pre-run state.
This means that variables set by one request in the runner are available to subsequent requests in the same run, but they don’t persist to your environment panel after the run ends unless you explicitly keep them.
Fixes
Fix 1: Check “Keep variable values” in the runner
In the Collection Runner, before clicking Run:
- Look for the “Keep variable values” option in the runner configuration panel.
- Check this box.
This tells the runner to write back any variable changes to your active environment after the run completes. Variables set by scripts during the run will be visible in your environment panel when the run finishes.
When to use this: When you want the runner to update shared state, such as refreshing an auth token that other tools or subsequent runs will use.
When not to use this: When you’re running multiple iterations and variables set in iteration 1 would pollute iteration 2. In that case, use data files or reset variables explicitly at the start of each iteration.
Fix 2: Use collection variables instead of environment variables for intra-run state
If a variable only needs to be shared between requests within the same collection run, use collection variables instead of environment variables:
// In the post-response script of your login request:
pm.collectionVariables.set('token', pm.response.json().access_token);
// In the pre-request script of subsequent requests:
const token = pm.collectionVariables.get('token');
Collection variables are always available in the runner regardless of whether an environment is selected. They persist for the duration of the collection run. They’re the right scope for values that are set and consumed within a single collection.
Fix 3: Ensure an environment is selected before running
pm.environment.set fails silently when no environment is active. Before running the collection:
- Open the Collection Runner.
- Verify that an environment is selected in the “Environment” dropdown.
- If you don’t need environment-level variables, use collection variables instead (Fix 2).
Fix 4: Use initial values for variables that should always exist
If a variable like baseUrl needs to be available from the very first request in a run, set it as the initial value in your environment, not just the current value.
In the environment editor:
- Set the “Initial value” field, not just the “Current value” field.
- The initial value is what the runner uses as the starting state.
If only the current value is set and the runner doesn’t have access to your local current values (for example, a teammate running the collection, or a Newman/Apidog CLI run), the variable starts empty.
Fix 5: Debug with console logging
Add console.log to your pre-request and post-response scripts to see exactly what’s happening:
// Pre-request script
console.log('token before request:', pm.collectionVariables.get('token'));
console.log('environment token:', pm.environment.get('token'));
Open the Postman Console (View > Show Postman Console) before running the collection. The logs will show you exactly which scope each variable is being read from and what value it holds at each step.
How Apidog handles variable scope
Apidog uses the same scope hierarchy: global, environment, collection, and local. The key difference is in the UI.
In Apidog’s variable editor, initial and current values are shown with explicit labels and colors. The interface makes it harder to accidentally set only the current value. When a script sets a variable using pm.environment.set (which Apidog supports for Postman compatibility), the environment panel updates visually in real time so you can see the change happen.
Apidog’s test runner also doesn’t reset variable values between steps unless you explicitly configure it to do so. The default behavior is to preserve variable state across requests in a run, which matches what most developers expect from manual testing.
For team scenarios, Apidog’s local-first model means environment variables are stored on disk. There’s no cloud sync that overwrites your current values between runs.
Common mistakes summary
| Mistake | Symptom | Fix |
|---|---|---|
| No environment selected | pm.environment.set silently fails |
Select an environment or use collection variables |
| Only current value set | Variable missing in CI or teammate’s machine | Set initial value in environment editor |
| Keep variable values unchecked | Variables reset after runner completes | Check “Keep variable values” in runner config |
| Using environment variables for intra-run state | Works in manual mode, fails in runner | Switch to pm.collectionVariables.set |
| Checking wrong scope in logs | Variable exists but wrong value used | Log both scopes and check resolution priority |
FAQ
Why does pm.environment.set work in manual mode but not the runner?In manual mode, you have a persistent environment session. In the runner, the environment is loaded at the start of the run and by default reset at the end. Scripts that set variables during the run still work for subsequent requests in that run, but the changes don’t persist to your environment unless “Keep variable values” is checked.
What’s the difference between pm.environment.set and pm.collectionVariables.set?pm.environment.set writes to the active environment, which is shared across all collections using that environment. pm.collectionVariables.set writes to a scope tied to the specific collection. For values that only matter within one collection run, collection variables are more appropriate and don’t require an environment to be selected.
Does this issue occur in Newman?Yes. Newman has the same scope model. By default, Newman starts each run with the exported environment’s initial values and doesn’t persist changes after the run unless you use the --export-environment flag to write the final environment state to a file.
What is the --export-environment flag in Newman?It tells Newman to write the environment’s final state, including any values set by scripts during the run, to a JSON file after the run completes. You can then pass that file as the environment for the next run. This is useful for pipelines where one run generates tokens used by the next.
Does Apidog support pm.collectionVariables.set?Yes. Apidog supports the full Postman scripting API, including pm.collectionVariables.set, pm.collectionVariables.get, pm.environment.set, and pm.environment.get. Collections migrated from Postman use the same script syntax.
How do I pass variables from one collection to another in Postman?Use global variables: pm.globals.set('token', value). Global variables persist across collections and environments for the lifetime of the Postman session. Be careful with this approach in team settings since global variables aren’t scoped and can create name collisions.
Variable scope issues in Postman are one of the most reliable sources of false negatives in API test suites. A test that passes manually but fails in the runner isn’t a test you can trust. Understanding the scope model, using the right setter for the right context, and checking “Keep variable values” when appropriate are the three moves that resolve most of these issues.



