Skip to content

Commit 1924bc3

Browse files
authored
fix(core): handleErrors should display error cause if it exists (#27886)
Some error messages are not displaying properly, as they pass their original message as a cause. While `node` supports this, our `handleErrors` function was not displaying error causes. ``` "Failed to process project graph. Run "nx reset" to fix this. Please report the issue if you keep seeing it. CreateMetadataError: The "test-plugin" plugin threw an error while creating metadata: cause message at /Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:17:29 at handleErrors (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.ts:11:26) at Object.<anonymous> (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:15:23) at Promise.then.completed (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/utils.js:298:28) at new Promise (<anonymous>) at callAsyncCircusFn (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/utils.js:231:10) at _callCircusTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:316:40) at async _runTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:252:3) at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:126:9) at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:121:9) at async run (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:71:3) at async runAndTransformResultsToJestFormat (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21) at async jestAdapter (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19) at async runTestInternal (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-runner/build/runTest.js:367:16) at async runTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-runner/build/runTest.js:444:34) Caused by: Error: cause message at /Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:16:21 at handleErrors (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.ts:11:26) at Object.<anonymous> (/Users/agentender/repos/nx/packages/nx/src/utils/handle-errors.spec.ts:15:23) at Promise.then.completed (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/utils.js:298:28) at new Promise (<anonymous>) at callAsyncCircusFn (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/utils.js:231:10) at _callCircusTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:316:40) at async _runTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:252:3) at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:126:9) at async _runTestsForDescribeBlock (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:121:9) at async run (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/run.js:71:3) at async runAndTransformResultsToJestFormat (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21) at async jestAdapter (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19) at async runTestInternal (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-runner/build/runTest.js:367:16) at async runTest (/Users/agentender/repos/nx/node_modules/.pnpm/[email protected]/node_modules/jest-runner/build/runTest.js:444:34)" ` ```
1 parent 68eeb2e commit 1924bc3

File tree

26 files changed

+203
-84
lines changed

26 files changed

+203
-84
lines changed

packages/nx/src/command-line/add/add.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { writeJsonFile } from '../../utils/fileutils';
99
import { logger } from '../../utils/logger';
1010
import { output } from '../../utils/output';
1111
import { getPackageManagerCommand } from '../../utils/package-manager';
12-
import { handleErrors } from '../../utils/params';
12+
import { handleErrors } from '../../utils/handle-errors';
1313
import { getPluginCapabilities } from '../../utils/plugins';
1414
import { nxVersion } from '../../utils/versions';
1515
import { workspaceRoot } from '../../utils/workspace-root';

packages/nx/src/command-line/affected/command-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
withRunOptions,
1010
withTargetAndConfigurationOption,
1111
} from '../yargs-utils/shared-options';
12-
import { handleErrors } from '../../utils/params';
12+
import { handleErrors } from '../../utils/handle-errors';
1313

1414
export const yargsAffectedCommand: CommandModule = {
1515
command: 'affected',

packages/nx/src/command-line/deprecated/command-objects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CommandModule } from 'yargs';
2-
import { handleErrors } from '../../utils/params';
2+
import { handleErrors } from '../../utils/handle-errors';
33
import {
44
withAffectedOptions,
55
withTargetAndConfigurationOption,

packages/nx/src/command-line/generate/generate.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ import {
1212
import { logger, NX_PREFIX } from '../../utils/logger';
1313
import {
1414
combineOptionsForGenerator,
15-
handleErrors,
1615
Options,
1716
Schema,
1817
} from '../../utils/params';
18+
import { handleErrors } from '../../utils/handle-errors';
1919
import { getLocalWorkspacePlugins } from '../../utils/plugins/local-plugins';
2020
import { printHelp } from '../../utils/print-help';
2121
import { workspaceRoot } from '../../utils/workspace-root';
22-
import { NxJsonConfiguration } from '../../config/nx-json';
2322
import { calculateDefaultProjectName } from '../../config/calculate-default-project-name';
2423
import { findInstalledPlugins } from '../../utils/plugins/installed-plugins';
2524
import { getGeneratorInformation } from './generator-utils';

packages/nx/src/command-line/import/command-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CommandModule } from 'yargs';
22
import { linkToNxDevAndExamples } from '../yargs-utils/documentation';
33
import { withVerbose } from '../yargs-utils/shared-options';
4-
import { handleErrors } from '../../utils/params';
4+
import { handleErrors } from '../../utils/handle-errors';
55

66
export const yargsImportCommand: CommandModule = {
77
command: 'import [sourceRepository] [destinationDirectory]',

packages/nx/src/command-line/login/login.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { verifyOrUpdateNxCloudClient } from '../../nx-cloud/update-manager';
22
import { getCloudOptions } from '../../nx-cloud/utilities/get-cloud-options';
3-
import { handleErrors } from '../../utils/params';
3+
import { handleErrors } from '../../utils/handle-errors';
44

55
export interface LoginArgs {
66
nxCloudUrl?: string;

packages/nx/src/command-line/logout/logout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { verifyOrUpdateNxCloudClient } from '../../nx-cloud/update-manager';
22
import { getCloudOptions } from '../../nx-cloud/utilities/get-cloud-options';
3-
import { handleErrors } from '../../utils/params';
3+
import { handleErrors } from '../../utils/handle-errors';
44

55
export interface LogoutArgs {
66
verbose?: boolean;

packages/nx/src/command-line/migrate/migrate.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
packageRegistryView,
5050
resolvePackageVersionUsingRegistry,
5151
} from '../../utils/package-manager';
52-
import { handleErrors } from '../../utils/params';
52+
import { handleErrors } from '../../utils/handle-errors';
5353
import {
5454
connectToNxCloudWithPrompt,
5555
onlyDefaultRunnerIsUsed,

packages/nx/src/command-line/new/new.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { flushChanges, FsTree } from '../../generators/tree';
2-
import { combineOptionsForGenerator, handleErrors } from '../../utils/params';
2+
import { combineOptionsForGenerator } from '../../utils/params';
3+
import { handleErrors } from '../../utils/handle-errors';
34
import { getGeneratorInformation } from '../generate/generator-utils';
45

56
function removeSpecialFlags(generatorOptions: { [p: string]: any }): void {

packages/nx/src/command-line/release/changelog.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { createProjectGraphAsync } from '../../project-graph/project-graph';
2525
import { interpolate } from '../../tasks-runner/utils';
2626
import { isCI } from '../../utils/is-ci';
2727
import { output } from '../../utils/output';
28-
import { handleErrors } from '../../utils/params';
28+
import { handleErrors } from '../../utils/handle-errors';
2929
import { joinPathFragments } from '../../utils/path';
3030
import { workspaceRoot } from '../../utils/workspace-root';
3131
import { ChangelogOptions } from './command-object';

packages/nx/src/command-line/release/plan-check.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
splitArgsIntoNxArgsAndOverrides,
88
} from '../../utils/command-line-utils';
99
import { output } from '../../utils/output';
10-
import { handleErrors } from '../../utils/params';
10+
import { handleErrors } from '../../utils/handle-errors';
1111
import { PlanCheckOptions, PlanOptions } from './command-object';
1212
import {
1313
createNxReleaseConfig,

packages/nx/src/command-line/release/plan.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
splitArgsIntoNxArgsAndOverrides,
1313
} from '../../utils/command-line-utils';
1414
import { output } from '../../utils/output';
15-
import { handleErrors } from '../../utils/params';
15+
import { handleErrors } from '../../utils/handle-errors';
1616
import { PlanOptions } from './command-object';
1717
import {
1818
createNxReleaseConfig,

packages/nx/src/command-line/release/publish.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
readGraphFileFromGraphArg,
1616
} from '../../utils/command-line-utils';
1717
import { output } from '../../utils/output';
18-
import { handleErrors } from '../../utils/params';
18+
import { handleErrors } from '../../utils/handle-errors';
1919
import { projectHasTarget } from '../../utils/project-graph-utils';
2020
import { generateGraph } from '../graph/graph';
2121
import { PublishOptions } from './command-object';

packages/nx/src/command-line/release/release.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { NxReleaseConfiguration, readNxJson } from '../../config/nx-json';
44
import { createProjectFileMapUsingProjectGraph } from '../../project-graph/file-map-utils';
55
import { createProjectGraphAsync } from '../../project-graph/project-graph';
66
import { output } from '../../utils/output';
7-
import { handleErrors } from '../../utils/params';
7+
import { handleErrors } from '../../utils/handle-errors';
88
import {
99
createAPI as createReleaseChangelogAPI,
1010
shouldCreateGitHubRelease,

packages/nx/src/command-line/release/version.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
readProjectsConfigurationFromProjectGraph,
2020
} from '../../project-graph/project-graph';
2121
import { output } from '../../utils/output';
22-
import { combineOptionsForGenerator, handleErrors } from '../../utils/params';
22+
import { combineOptionsForGenerator } from '../../utils/params';
2323
import { joinPathFragments } from '../../utils/path';
2424
import { workspaceRoot } from '../../utils/workspace-root';
2525
import { parseGeneratorString } from '../generate/generate';
@@ -52,6 +52,7 @@ import {
5252
createGitTagValues,
5353
handleDuplicateGitTags,
5454
} from './utils/shared';
55+
import { handleErrors } from '../../utils/handle-errors';
5556

5657
const LARGE_BUFFER = 1024 * 1000000;
5758

packages/nx/src/command-line/repair/repair.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { handleErrors } from '../../utils/params';
1+
import { handleErrors } from '../../utils/handle-errors';
22
import * as migrationsJson from '../../../migrations.json';
33
import { executeMigrations } from '../migrate/migrate';
44
import { output } from '../../utils/output';

packages/nx/src/command-line/run-many/command-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
withOverrides,
88
withBatch,
99
} from '../yargs-utils/shared-options';
10-
import { handleErrors } from '../../utils/params';
10+
import { handleErrors } from '../../utils/handle-errors';
1111

1212
export const yargsRunManyCommand: CommandModule = {
1313
command: 'run-many',

packages/nx/src/command-line/run/command-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
withOverrides,
55
withRunOneOptions,
66
} from '../yargs-utils/shared-options';
7-
import { handleErrors } from '../../utils/params';
7+
import { handleErrors } from '../../utils/handle-errors';
88

99
export const yargsRunCommand: CommandModule = {
1010
command: 'run [project][:target][:configuration] [_..]',

packages/nx/src/command-line/run/run.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { env as appendLocalEnv } from 'npm-run-path';
2-
import {
3-
combineOptionsForExecutor,
4-
handleErrors,
5-
Schema,
6-
} from '../../utils/params';
2+
import { combineOptionsForExecutor, Schema } from '../../utils/params';
3+
import { handleErrors } from '../../utils/handle-errors';
74
import { printHelp } from '../../utils/print-help';
85
import { NxJsonConfiguration } from '../../config/nx-json';
96
import { relative } from 'path';

packages/nx/src/command-line/show/command-object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
withAffectedOptions,
66
withVerbose,
77
} from '../yargs-utils/shared-options';
8-
import { handleErrors } from '../../utils/params';
8+
import { handleErrors } from '../../utils/handle-errors';
99

1010
export interface NxShowArgs {
1111
json?: boolean;

packages/nx/src/command-line/sync/sync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as ora from 'ora';
22
import { readNxJson } from '../../config/nx-json';
33
import { createProjectGraphAsync } from '../../project-graph/project-graph';
44
import { output } from '../../utils/output';
5-
import { handleErrors } from '../../utils/params';
5+
import { handleErrors } from '../../utils/handle-errors';
66
import {
77
collectAllRegisteredSyncGenerators,
88
flushSyncGeneratorChanges,

packages/nx/src/project-graph/error-types.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class ProjectGraphError extends Error {
4242
this.#partialProjectGraph = partialProjectGraph;
4343
this.#partialSourceMaps = partialSourceMaps;
4444
this.stack = `${this.message}\n ${errors
45-
.map((error) => error.stack.split('\n').join('\n '))
45+
.map((error) => indentString(formatErrorStackAndCause(error), 2))
4646
.join('\n')}`;
4747
}
4848

@@ -263,23 +263,29 @@ export class MergeNodesError extends Error {
263263
this.name = this.constructor.name;
264264
this.file = file;
265265
this.pluginName = pluginName;
266-
this.stack = `${this.message}\n ${error.stack.split('\n').join('\n ')}`;
266+
this.stack = `${this.message}\n${indentString(
267+
formatErrorStackAndCause(error),
268+
2
269+
)}`;
267270
}
268271
}
269272

270273
export class CreateMetadataError extends Error {
271274
constructor(public readonly error: Error, public readonly plugin: string) {
272-
super(`The "${plugin}" plugin threw an error while creating metadata:`, {
273-
cause: error,
274-
});
275+
super(
276+
`The "${plugin}" plugin threw an error while creating metadata: ${error.message}`,
277+
{
278+
cause: error,
279+
}
280+
);
275281
this.name = this.constructor.name;
276282
}
277283
}
278284

279285
export class ProcessDependenciesError extends Error {
280286
constructor(public readonly pluginName: string, { cause }) {
281287
super(
282-
`The "${pluginName}" plugin threw an error while creating dependencies:`,
288+
`The "${pluginName}" plugin threw an error while creating dependencies: ${cause.message}`,
283289
{
284290
cause,
285291
}
@@ -316,7 +322,7 @@ export class ProcessProjectGraphError extends Error {
316322
}
317323
);
318324
this.name = this.constructor.name;
319-
this.stack = `${this.message}\n ${cause.stack.split('\n').join('\n ')}`;
325+
this.stack = `${this.message}\n${indentString(cause, 2)}`;
320326
}
321327
}
322328

@@ -394,3 +400,24 @@ export class LoadPluginError extends Error {
394400
this.name = this.constructor.name;
395401
}
396402
}
403+
404+
function indentString(str: string, indent: number): string {
405+
return (
406+
' '.repeat(indent) +
407+
str
408+
.split('\n')
409+
.map((line) => ' '.repeat(indent) + line)
410+
.join('\n')
411+
);
412+
}
413+
414+
function formatErrorStackAndCause(error: Error): string {
415+
const cause =
416+
error.cause && error.cause instanceof Error ? error.cause : null;
417+
return (
418+
error.stack +
419+
(cause
420+
? `\nCaused by: \n${indentString(cause.stack ?? cause.message, 2)}`
421+
: '')
422+
);
423+
}

packages/nx/src/tasks-runner/run-command.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { isRelativePath } from '../utils/fileutils';
2020
import { isCI } from '../utils/is-ci';
2121
import { isNxCloudUsed } from '../utils/nx-cloud-utils';
2222
import { output } from '../utils/output';
23-
import { handleErrors } from '../utils/params';
23+
import { handleErrors } from '../utils/handle-errors';
2424
import {
2525
collectEnabledTaskSyncGeneratorsFromTaskGraph,
2626
flushSyncGeneratorChanges,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {
2+
CreateMetadataError,
3+
ProjectGraphError,
4+
} from '../project-graph/error-types';
5+
import { handleErrors } from './handle-errors';
6+
import { output } from './output';
7+
8+
describe('handleErrors', () => {
9+
afterEach(() => {
10+
jest.restoreAllMocks();
11+
});
12+
13+
it('should display project graph error cause message', async () => {
14+
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
15+
await handleErrors(true, async () => {
16+
const cause = new Error('cause message');
17+
const metadataError = new CreateMetadataError(cause, 'test-plugin');
18+
throw new ProjectGraphError(
19+
[metadataError],
20+
{ nodes: {}, dependencies: {} },
21+
{}
22+
);
23+
});
24+
const { bodyLines, title } = spy.mock.calls[0][0];
25+
const body = bodyLines.join('\n');
26+
expect(body).toContain('cause message');
27+
expect(body).toContain('test-plugin');
28+
});
29+
30+
it('should only display wrapper error if not verbose', async () => {
31+
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
32+
await handleErrors(false, async () => {
33+
const cause = new Error('cause message');
34+
const metadataError = new CreateMetadataError(cause, 'test-plugin');
35+
throw new ProjectGraphError(
36+
[metadataError],
37+
{ nodes: {}, dependencies: {} },
38+
{}
39+
);
40+
});
41+
42+
const { bodyLines, title } = spy.mock.calls[0][0];
43+
const body = bodyLines.join('\n');
44+
expect(body).not.toContain('cause message');
45+
});
46+
47+
it('should display misc errors that do not have a cause', async () => {
48+
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
49+
await handleErrors(true, async () => {
50+
throw new Error('misc error');
51+
});
52+
const { bodyLines, title } = spy.mock.calls[0][0];
53+
const body = bodyLines.join('\n');
54+
expect(body).toContain('misc error');
55+
expect(body).not.toMatch(/[Cc]ause/);
56+
});
57+
58+
it('should display misc errors that have a cause', async () => {
59+
const spy = jest.spyOn(output, 'error').mockImplementation(() => {});
60+
await handleErrors(true, async () => {
61+
const cause = new Error('cause message');
62+
const err = new Error('misc error', { cause });
63+
throw err;
64+
});
65+
const { bodyLines, title } = spy.mock.calls[0][0];
66+
const body = bodyLines.join('\n');
67+
expect(body).toContain('misc error');
68+
expect(body).toContain('cause message');
69+
});
70+
});

0 commit comments

Comments
 (0)