Skip to content

Fix opaque "Process exited with code 1" in VS Code workspace view#9136

Open
vhvb1989 wants to merge 2 commits into
mainfrom
fix/vscode-show-error-surfacing
Open

Fix opaque "Process exited with code 1" in VS Code workspace view#9136
vhvb1989 wants to merge 2 commits into
mainfrom
fix/vscode-show-error-surfacing

Conversation

@vhvb1989

@vhvb1989 vhvb1989 commented Jul 14, 2026

Copy link
Copy Markdown
Member

Warning

Blocked by #9140 — the azure-dev - vscode ADO pipeline currently fails for all ext/vscode PRs due to npm ci --no-optional dropping esbuild's platform binary (unrelated to this change). GitHub build-test is green on all 3 OSes for this commit.

Fixes #9130 (and its duplicate #9131).

Problem

Customers received an opaque error notification with a Report Issue button:

Action: azure-dev.views.workspace.application.resolve
Error type: 1
Error Message: Process exited with code 1
Call Stack: ChildProcess.<anonymous> extension.js:2:535569

Root cause

The Azure Resources workspace tree view resolves each application node by running
azd show --no-prompt --cwd <dir> --output json via execAsync
(AzureDevCliApplication.getResultsWorkspaceAzureDevShowProvider.getShowResults).

  1. When azd show exits non-zero, spawnStreamAsync rejects from inside a ChildProcess
    exit handler with a generic Process exited with code 1 message (hence the
    ChildProcess.<anonymous> call stack) — before execAsync ever reads the accumulated
    stderr. So the real azd error (on stderr) is silently discarded, and the existing
    azure.yaml error classification in getShowResults can never match.
  2. getResults did not set suppressDisplay, so this background tree resolution surfaced a
    modal error with a Report Issue prompt. Routine states (not logged in, no environment yet,
    invalid azure.yaml) therefore looked like extension bugs and got auto-reported (Process exited with code 1 #9130, Process exited with code 1 #9131).

Fix

  • execAsync.ts — on a non-zero exit, append the child process's stderr to the thrown
    error's message. AccumulatorStream.getString() awaits the stream close event, so stderr is
    fully flushed (no race). This surfaces the real reason and makes the existing classification in
    AzureDevShowProvider effective.
  • AzureDevCliApplication.getResults — set errorHandling.suppressDisplay = true for this
    passive tree resolution so expected/environmental failures no longer pop a Report Issue dialog.
    Telemetry still records the failure for real triage, and azure.yaml problems continue to be
    reported via the Problems panel.
  • Tests — added execAsync unit tests (success, stderr-on-failure, empty-stderr failure).

Validation

  • npm run lint
  • npx tsc --noEmit
  • npx cspell "src/**/*.ts" --config .vscode/cspell.yaml ✓ (0 issues)
  • npm run build (esbuild + tsc) ✓
  • npm test (vscode-test) could not run in this environment (requires a display/Xvfb, which is
    unavailable). The new tests use the current Node.js executable to exercise the real
    spawnStreamAsync path cross-platform.

@vhvb1989
vhvb1989 force-pushed the fix/vscode-show-error-surfacing branch from 7adc968 to b19aa8e Compare July 14, 2026 16:36
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
21 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@vhvb1989

Copy link
Copy Markdown
Member Author

/azp run azure-dev - vscode

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@vhvb1989

Copy link
Copy Markdown
Member Author

🚧 Blocked by #9140. The remaining red check (azure-dev - vscode on ADO) is a pre-existing pipeline/tooling failure (esbuild platform binary dropped by npm ci --no-optional), not caused by this PR. GitHub build-test (same ci-test.ps1) passes on ubuntu/macos/windows for this commit. This PR can merge once #9140 is resolved.

vhvb1989 and others added 2 commits July 20, 2026 18:50
The Azure Resources workspace tree resolves each application node by
running `azd show --no-prompt --output json` via execAsync. When azd
exits non-zero, spawnStreamAsync rejects from a ChildProcess handler
with a generic "Process exited with code 1" message *before* the caller
reads stderr, so the real azd error is discarded. The failure is then
reported as a modal error with a "Report Issue" button, prompting users
to file bugs (e.g. #9130, #9131) for routine states like not being
logged in or having no environment yet.

- execAsync: on non-zero exit, append the child process's stderr to the
  error message so callers can classify and report the real failure
  instead of an opaque exit code. This also makes the existing
  azure.yaml error classification in AzureDevShowProvider effective.
- AzureDevCliApplication.getResults: suppress the error display for this
  background tree resolution so expected/environmental failures no
  longer surface a Report Issue popup. Telemetry still records them.
- Add execAsync unit tests covering success, stderr-on-failure, and
  empty-stderr failure paths.

Fixes #9130

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8907c2b4-8adc-4280-8f11-af43d905644f
The previous execAsync test spawned a real `node -e` subprocess, which
failed on Windows CI: NoShell stripped the inner double quotes from the
inline script (`process.stdout.write(out)` -> ReferenceError). Relying on
a real process / process.execPath inside the extension host is fragile
and platform-dependent.

Add an optional injectable spawn function to execAsync (defaulting to the
real spawnStreamAsync) and rewrite the tests to inject a fake that writes
to the accumulator pipes and resolves/rejects. This exercises the real
stderr-appending logic deterministically with no subprocess, so it passes
identically on Linux, macOS, and Windows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8907c2b4-8adc-4280-8f11-af43d905644f
@vhvb1989
vhvb1989 force-pushed the fix/vscode-show-error-surfacing branch from 82e3535 to 304a3cd Compare July 20, 2026 18:51
@vhvb1989
vhvb1989 marked this pull request as ready for review July 20, 2026 18:52
Copilot AI review requested due to automatic review settings July 20, 2026 18:52
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
21 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@github-actions

Copy link
Copy Markdown

📋 Prioritization Note

Thanks for the contribution! The linked issue isn't in the current milestone yet.
Thank you for logging this issue; our team is reviewing it. If you need urgent prioritization, tag @RickWinter and @kristenwomack to let us know.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves VS Code workspace-view error handling for failed azd show calls.

Changes:

  • Suppresses background tree-resolution error dialogs.
  • Includes stderr in process-exit errors.
  • Adds unit tests and release notes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
AzureDevCliApplication.ts Suppresses passive resolution dialogs.
execAsync.ts Appends stderr to exit errors.
execAsync.test.ts Tests process output and failures.
CHANGELOG.md Documents the fix.

// with code 1") from within a ChildProcess handler, before the caller ever reads stderr. Append
// the process's stderr to the error so callers can classify and report the real failure instead
// of an opaque exit code.
const stderr = (await stderrFinal.getString()).trim();
Comment on lines +20 to +24
options?.stdErrPipe?.write(Buffer.from(stderr));
}
options?.stdErrPipe?.end();

return rejectWith ? Promise.reject(rejectWith) : Promise.resolve();
@azure-sdk-automation

Copy link
Copy Markdown
Contributor

VSCode Extension Installation Instructions

  1. Download the extension at https://azuresdkartifacts.z5.web.core.windows.net/azd/vscode/pr/9136/azure-dev-0.11.0-alpha.1.vsix
  2. Extract the extension from the compressed file
  3. In vscode
    a. Open "Extensions" (Ctrl+Shift+X)
    b. Click the ...\ menu at top of Extensions sidebar
    c. Click "Install from VSIX"
    d. Select location of downloaded file

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One issue on the error-surfacing change, the rest looks fine.

The new catch block in execAsync can hang for any rejection that isn't a non-zero process exit (details inline). I confirmed it against @microsoft/vscode-processutils 0.2.1: with this change an ENOENT command (azd not on PATH) never settles, where the old await spawnStreamAsync(...) rejected right away. Gating the stderr read on isChildProcessError keeps the fix for the code-1 case while letting pre-spawn and spawn-error failures rethrow.

The suppressDisplay change and CHANGELOG entry look good.

// with code 1") from within a ChildProcess handler, before the caller ever reads stderr. Append
// the process's stderr to the error so callers can classify and report the real failure instead
// of an opaque exit code.
const stderr = (await stderrFinal.getString()).trim();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified this hangs. stderrFinal.getString() only resolves on the stream's close event, which needs .end() called on stdErrPipe. spawnStreamAsync only pipes and ends that stream after the process actually spawns and exits, so for a pre-spawn throw (cancelled token) or a spawn error event (ENOENT when azd is not on PATH) the pipe is never ended and this await never settles. The caller then hangs instead of receiving the original error.

Reproduced against @microsoft/vscode-processutils 0.2.1: an ENOENT command rejects immediately with the old await spawnStreamAsync(...), but with this catch block it never resolves or rejects. Same for a pre-cancelled token. Since azd-not-on-PATH is a common first-run state, this turns a clean not found error into an indefinite hang on the workspace tree node.

Only the non-zero-exit path guarantees stdErrPipe was ended, so gate the stderr read on it and let everything else rethrow:

} catch (error) {
    // Only a non-zero process exit (ChildProcessError) guarantees stdErrPipe was ended, so only
    // then is it safe to await it. Pre-spawn failures (cancelled token, ENOENT) never end the pipe.
    if (isChildProcessError(error)) {
        const stderr = (await stderrFinal.getString()).trim();
        if (stderr) {
            error.message = `${error.message}\n${stderr}`.trim();
        }
    }

    throw error;
}

isChildProcessError is exported from @microsoft/vscode-processutils.

);
expect((error as Error).message, 'Error should surface the underlying stderr').to.include(
'parsing project file: File is empty.'
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This failure test writes and ends stderr before returning the rejected promise, so it can't catch the hang above. A regression test that leaves the stderr pipe open when it rejects (or drives the real spawn path with a bogus command) would fail on the current code and pass once the stderr read is gated on the exit case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Process exited with code 1

4 participants