TL;DR
Variables that work during manual Postman requests can disappear when you run the same collection in the Collection Runner. The usual cause is a variable scope mismatch: pm.environment.set() writes to the active environment, while runner behavior depends on environment selection, initial/current values, and the “Keep variable values” option. Use the right scope, verify your runner config, and log values before each request.
Introduction
You test an API manually in Postman:
- Run a login request.
- A script saves the auth token.
- Subsequent requests use
{{token}}. - Everything passes.
Then you click Run Collection.
The login request succeeds, but the next request returns 401 Unauthorized. The token is missing.
This usually happens because the token is written to a scope that behaves differently in the Collection Runner than it does during manual testing. Once you understand Postman’s scope hierarchy and runner behavior, the fix is straightforward.
Postman variable scopes
Postman resolves variables by priority. If multiple scopes contain the same variable name, the highest-priority scope wins.
From highest to lowest priority:
- Local variables — available only during the current script execution.
- Data variables — loaded from a CSV or JSON file for runner iterations.
- Collection variables — scoped to the collection.
- Environment variables — scoped to the selected environment.
- Global variables — available across collections and environments.
When Postman sees:
Authorization: Bearer {{token}}
it searches those scopes in order and uses the first token it finds.
That means a token in a data file can override a token in your collection or environment. It also means you need to write variables to the scope you actually intend to read from.
Why variables disappear in the Collection Runner
1. Current value and initial value are not the same
Postman variables have two fields:
- Initial value: synced/shared value.
- Current value: local value on your machine.
When you run this script:
pm.environment.set('token', pm.response.json().access_token);
Postman updates the variable’s current value in the active environment.
During manual testing, that current value is available across requests in your local session.
In the Collection Runner, Postman loads the environment state at the start of the run. Depending on runner settings, values changed during the run may not be written back after the run completes.
2. pm.environment.set() requires an active environment
This script writes to the selected environment:
pm.environment.set('token', pm.response.json().access_token);
If no environment is selected in the runner, there is no environment to write to.
Result: the token is not available where you expect it.
Before using pm.environment.set(), confirm that the runner has an environment selected.
3. “Keep variable values” controls post-run persistence
The Collection Runner has a Keep variable values option.
If it is unchecked, variables changed during the run can be reset after the run completes.
Important distinction:
- Variables set during the run can be available to later requests in the same run.
- They may not persist back to the environment after the run finishes unless Keep variable values is enabled.
4. Manual mode and runner mode use different execution contexts
In manual mode, you usually run requests inside one persistent workspace context.
In the Collection Runner, Postman creates a run context:
- Load collection.
- Load environment/data.
- Execute requests.
- Apply runner cleanup behavior.
So a token that appears stable during manual testing may not persist after a runner execution.
Fix 1: Enable “Keep variable values”
Use this when you want scripts in the runner to update your environment.
Steps:
- Open the Collection Runner.
- Select the target collection.
- Select the correct environment.
- Enable Keep variable values.
- Run the collection.
Use this for values such as refreshed tokens that should remain available after the run.
Avoid it when running multiple iterations where values from one iteration could pollute the next.
Fix 2: Use collection variables for state shared inside one collection run
If the token is only needed by requests in the same collection, use collection variables instead of environment variables.
In the login request’s post-response script:
const json = pm.response.json();
pm.collectionVariables.set('token', json.access_token);
In later requests:
const token = pm.collectionVariables.get('token');
pm.request.headers.upsert({
key: 'Authorization',
value: `Bearer ${token}`
});
Or use the variable directly in a header:
Authorization: Bearer {{token}}
Collection variables are a better fit when:
- The value is generated during the collection run.
- The value is only used by that collection.
- You do not want to depend on an environment being selected.
Fix 3: Select an environment before running
If your scripts use pm.environment.set() or pm.environment.get(), the runner needs an active environment.
Checklist:
- Open the runner.
- Find the Environment dropdown.
- Select the same environment you use during manual testing.
- Run the collection again.
Example script:
const token = pm.response.json().access_token;
pm.environment.set('token', token);
Then use it in later requests:
Authorization: Bearer {{token}}
If you do not need environment-level state, prefer:
pm.collectionVariables.set('token', token);
Fix 4: Set initial values for variables that must exist at run start
Some variables must exist before the first request runs, such as:
baseUrlclientIdtenantId- static test account IDs
For those, do not rely only on current values.
In the environment editor:
- Open your environment.
- Set the variable’s Initial value.
- Set the Current value if needed for local use.
- Save the environment.
Example:
| Variable | Initial value | Current value |
|---|---|---|
baseUrl |
https://api.example.com |
https://api.example.com |
clientId |
my-client-id |
my-client-id |
token |
empty | generated during run |
A generated token usually belongs in the current value or collection variable. A required config value should have an initial value.
Fix 5: Log each scope while debugging
Add logging to see where Postman is reading from and writing to.
Pre-request script:
console.log('collection token:', pm.collectionVariables.get('token'));
console.log('environment token:', pm.environment.get('token'));
console.log('global token:', pm.globals.get('token'));
Post-response script:
const json = pm.response.json();
console.log('access_token from response:', json.access_token);
pm.collectionVariables.set('token', json.access_token);
console.log('token after set:', pm.collectionVariables.get('token'));
Then open:
View > Show Postman Console
Run the collection and inspect the logs request by request.
This helps you catch common issues:
- The token is written to environment scope but read from collection scope.
- A data variable overrides the environment variable.
- The login response does not contain the field you expect.
- No environment is selected.
Recommended pattern for auth tokens
For most collection-level API tests, use this pattern.
Login request test script:
const response = pm.response.json();
pm.test('login returns access token', function () {
pm.expect(response.access_token).to.be.a('string').and.not.empty;
});
pm.collectionVariables.set('token', response.access_token);
Protected request pre-request script:
const token = pm.collectionVariables.get('token');
pm.test('token is available', function () {
pm.expect(token).to.be.a('string').and.not.empty;
});
pm.request.headers.upsert({
key: 'Authorization',
value: `Bearer ${token}`
});
This keeps the token inside the collection run and avoids depending on environment persistence.
How Apidog handles variable scope
Apidog supports Postman-compatible scripting, including:
pm.environment.set('token', value);
pm.environment.get('token');
pm.collectionVariables.set('token', value);
pm.collectionVariables.get('token');
Apidog uses variable scopes such as global, environment, collection, and local. Its variable editor labels initial and current values clearly, which helps reduce accidental scope mistakes.
When using Postman-compatible scripts such as pm.environment.set(), Apidog updates the environment panel visually so you can see variable changes while testing.
Its runner preserves variable state across requests in a run unless you configure it otherwise, which aligns with how many developers expect manual API testing to behave.
Common mistakes summary
| Mistake | Symptom | Fix |
|---|---|---|
| No environment selected |
pm.environment.set() does not write where expected |
Select an environment or use collection variables |
| Only current value is set | Variable missing for teammates, CI, or exported runs | Set the initial value |
| “Keep variable values” unchecked | Variables reset after runner completes | Enable “Keep variable values” |
| Environment variables used for intra-run state | Works manually but fails or resets in runner | Use pm.collectionVariables.set()
|
| Wrong scope checked in logs | Variable exists but request uses another value | Log collection, environment, and global scopes |
| Duplicate variable names | Unexpected value is resolved | Rename variables or check scope priority |
FAQ
Why does pm.environment.set() work manually but not in the runner?
Manual testing uses your active environment session. The Collection Runner loads an environment at run start and may reset values after the run. If you want runner changes to persist, select an environment and enable Keep variable values.
What is the difference between pm.environment.set() and pm.collectionVariables.set()?
pm.environment.set() writes to the active environment.
pm.environment.set('token', value);
pm.collectionVariables.set() writes to the collection scope.
pm.collectionVariables.set('token', value);
Use environment variables for shared environment configuration. Use collection variables for values generated and consumed inside one collection.
Does this issue happen in Newman?
Yes. Newman uses the same scope model. By default, a run starts from the provided environment file and does not automatically persist changes unless you export the final environment.
Use:
newman run collection.json \
-e environment.json \
--export-environment environment.after-run.json
Then pass the exported file into the next run if needed.
What does --export-environment do in Newman?
It writes the final environment state after the run to a JSON file, including values set by scripts.
Example:
newman run collection.json \
-e environment.json \
--export-environment updated-environment.json
This is useful when one pipeline step generates values needed by another step.
Does Apidog support pm.collectionVariables.set()?
Yes. Apidog supports Postman-compatible scripting APIs, including:
pm.collectionVariables.set('token', value);
pm.collectionVariables.get('token');
pm.environment.set('token', value);
pm.environment.get('token');
How do I pass variables from one collection to another in Postman?
Use a broader scope, such as globals:
pm.globals.set('token', value);
Then read it from another collection:
const token = pm.globals.get('token');
Use globals carefully. They are not scoped to a collection or environment, so name collisions are easier to create.
Final checklist
When a variable works manually but fails in the Collection Runner, check these in order:
- Is the correct environment selected?
- Is the script writing to the same scope the request reads from?
- Is Keep variable values enabled if you need post-run persistence?
- Should the value be a collection variable instead of an environment variable?
- Are required startup values set as initial values?
- Do console logs show the expected value before the failing request?
Variable scope issues create noisy API test failures. Use collection variables for intra-run state, environment variables for environment config, and logging to verify the exact scope being used.
Top comments (0)