Skip to content

Bug: PM2 passes object properties as "[object Object]" environment variables to subprocesses #6073

@SenZmaKi

Description

@SenZmaKi

PM2 is polluting the environment variables of spawned subprocesses by passing object properties that get stringified to "[object Object]"

All spawned processes receive corrupted environment variables like env="[object Object]", axm_monitor="[object Object]", etc.

Environment

  • PM2 Version: (6.0.14)
  • Node.js Version: (v24.11.1)
  • Operating System: (affects all platforms)
  • Interpreter: All interpreters (Node.js, Python, Ruby, PHP, etc.)

Steps to Reproduce

  1. Create a Node.js script (test.js):
console.log("env variable:", process.env.env);
console.log("axm_monitor:", process.env.axm_monitor);
console.log("Type of env:", typeof process.env.env);
  1. Create a PM2 config (ecosystem.config.js):
module.exports = {
  apps: [{
    name: "test-node",
    script: "./test.js",
    interpreter: "node",
    env: {
      MY_VAR: "hello"
    }
  }]
};
  1. Run with PM2:
pm2 start ecosystem.config.js
pm2 logs test-node

Output:

env variable: [object Object]
axm_monitor: [object Object]
Type of env: string

Root Cause Analysis

The Flow

  1. User config contains env: { MY_VAR: "hello" } as an object
  2. God.executeApp() (lib/God.js:178) calls:
    `ALL interpreters affected**: Node.js, Python, Ruby, PHP, etc. all receive corrupted environment variables
  • Environment variable name collision: If a user actually needs an env var named env, it's overwritten with "[object Object]"
  • Debugging confusion: Users see mysterious [object Object] values in their applications
  • Silent data corruption: Applications may silently fail or behave incorrectly when checking for these environment variables
  1. PM2 adds metadata objects (lib/God.js:183-186):

    env_copy['axm_actions']    = [];
    env_copy['axm_monitor']    = {};
    env_copy['axm_options']    = {};
    env_copy['axm_dynamic']    = {};
  2. ForkMode.forkMode() (lib/God/ForkMode.js:97) passes the entire pm2_env to spawn():

    var options = {
      env: pm2_env,  // Contains nested objects!
      detached: true,
      cwd: pm2_env.pm_cwd,
      stdio: ['pipe', 'pipe', 'pipe', 'ipc']
    }
    spawn(command, args, options);
  3. Node.js spawn() calls .toString() on all values in the env object

  4. Objects become "[object Object]" when stringified

The 6 Polluted Environment Variables

  1. env - The original config env object (still present after spreading)
  2. axm_monitor - PM2 metrics object
  3. axm_options - PM2 monitoring options
  4. axm_dynamic - PM2 dynamic configuration
  5. axm_actions - PM2 actions array
  6. node_args - Node arguments array

Impact

  • Interpreters receive corrupted environment variables
  • Environment variable name collision: If a user actually needs an env var named env, it's overwritten with "[object Object]"
  • Debugging confusion: Users see mysterious [object Object] values in their applications

Proposed Solutions

Solution 1: Clean up objects after spreading (Minimal change)

In lib/God.js after line 178:

Utility.extend(env_copy, env_copy.env);

// Remove object properties that shouldn't be environment variables
delete env_copy.env;
// Note: axm_* objects are added later but should also be filtered before spawn

Then in lib/God/ForkMode.js before spawn:

// Create clean environment with only primitive values
var spawnEnv = {};
for (var key in pm2_env) {
  var type = typeof pm2_env[key];
  if (type === 'string' || type === 'number' || type === 'boolean') {
    spawnEnv[key] = String(pm2_env[key]);
  }
}

var options = {
  env: spawnEnv,  // Only primitives
  detached: true,
  cwd: pm2_env.pm_cwd,
  stdio: ['pipe', 'pipe', 'pipe', 'ipc']
}

Solution 2: Separate pm2_env from spawn env (Better design)

Store PM2 metadata separately and only pass actual environment variables to spawn:

// In God.js, separate concerns
var pm2_metadata = {
  axm_monitor: {},
  axm_options: {},
  axm_dynamic: {},
  axm_actions: []
};

// Keep env_copy clean for spawn
// Store metadata in process object separately

Workaround

There's currently no clean workaround except to:

  1. Avoid using environment variable names that conflict with PM2's internal objects (env, axm_monitor, axm_options, axm_dynamic, axm_actions, node_args)
  2. Filter out "[object Object]" values in your application code
  3. Check if an environment variable equals "[object Object]" before using it

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions