Skip to content

FORCE_COLOR=0 is deleted then re-introduced as 'true' for forked child tasks #35292

@comp615

Description

@comp615

Current Behavior

When FORCE_COLOR=0 is set in the environment (common in CI to suppress ANSI codes), Nx deletes it in bin/nx.ts and then re-introduces FORCE_COLOR=true for every forked child task. This causes all child processes (ESLint, TypeScript, Vitest, etc.) to emit ANSI escape codes even though the user explicitly requested no colors.

Root Cause

The workaround for picocolors#100 introduced in PR #34520 deletes FORCE_COLOR from the parent process, but task-env.ts defaults undefined to 'true':

Step 1 — bin/nx.ts (lines 4-8):

if (process.env.FORCE_COLOR === '0') {
  process.env.NO_COLOR = '1';
  delete process.env.FORCE_COLOR; // FORCE_COLOR is now undefined
}

Step 2 — task-env.ts (line 19) and task-orchestrator.ts (lines 681, 1000):

process.env.FORCE_COLOR === undefined ? 'true' : process.env.FORCE_COLOR
// Since FORCE_COLOR was deleted → evaluates to 'true'

Step 3 — task-env.ts (line 76):

FORCE_COLOR: forceColor, // 'true' is injected into every child's env

The child process receives both FORCE_COLOR=true and NO_COLOR=1, but per the force-color spec and Node.js docs, FORCE_COLOR takes precedence over NO_COLOR.

Expected Behavior

FORCE_COLOR=0 should propagate to child tasks, resulting in no ANSI escape codes in their output.

Impact

This affects any CI environment that sets FORCE_COLOR=0 to get parseable plain-text output. In our case, ANSI escape codes in ESLint output broke downstream log parsers that use regex to extract lint errors (the ✖ N problems (M errors, O warnings) summary line contains invisible ANSI bytes that prevent matching).

Suggested Fix

Preserve the user's original FORCE_COLOR intent before deleting it, and consult the saved value when building child environments:

// bin/nx.ts — save intent before deleting
if (process.env.FORCE_COLOR === '0') {
  process.env.NX_ORIGINAL_FORCE_COLOR = '0';
  process.env.NO_COLOR = '1';
  delete process.env.FORCE_COLOR;
}
// task-env.ts / task-orchestrator.ts — check saved value
process.env.FORCE_COLOR === undefined
  ? (process.env.NX_ORIGINAL_FORCE_COLOR === '0' ? '0' : 'true')
  : process.env.FORCE_COLOR

Workaround

Setting FORCE_COLOR=false (string) instead of FORCE_COLOR=0 bypasses the === '0' check in bin/nx.ts, so the value passes through to children unchanged. However, 'false' is not a standard FORCE_COLOR value per either force-color.org or the chalk/supports-color convention.

Environment

🤖 This issue was drafted with the help of AI

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions