Skip to content

fix(task): avoid duplicated stderr on task failure in replacing mode#8275

Merged
jdx merged 1 commit intomainfrom
fix/task-failure-output-duplication
Feb 21, 2026
Merged

fix(task): avoid duplicated stderr on task failure in replacing mode#8275
jdx merged 1 commit intomainfrom
fix/task-failure-output-duplication

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 21, 2026

Summary

  • When a task fails in --output replacing mode, on_error() re-printed all accumulated output (stdout + stderr) via pr.println(), but stderr was already printed during execution, causing duplication
  • Fix by tagging each output line with its source (stdout vs stderr) and only replaying stdout lines in on_error(), since stdout was hidden behind the progress indicator (pr.set_message()) while stderr was already permanently visible

Fixes #8267

Test plan

  • Verified --output replacing no longer duplicates stderr on task failure
  • Verified stdout is still shown on failure (it was hidden behind progress indicator)
  • Verified all other output modes (default, prefix, interleave, raw, quiet, silent, timed, keep-order) are unaffected
  • Verified successful tasks are unaffected
  • All 481 unit tests pass

🤖 Generated with Claude Code


Note

Low Risk
Small, localized change to command output handling on failures; main risk is inadvertently suppressing or mis-ordering error output in edge cases.

Overview
Fixes a failure-mode logging bug where --output replacing could duplicate stderr by replaying all captured output on error.

CmdLineRunner now tags captured lines with an OutputSource and updates on_error to replay only stdout (and only when stdout was hidden behind the progress indicator), leaving stderr to be shown just once during execution. Also updates the raw-exec error path to pass an empty output vector and tweaks contributor guidance in CLAUDE.md to prefer the task scope for task-related changes.

Written by Cursor Bugbot for commit 5b1339e. This will update automatically on new commits. Configure here.

Copilot AI review requested due to automatic review settings February 21, 2026 14:48
@jdx jdx changed the title fix(run): avoid duplicated stderr on task failure in replacing mode fix(task): avoid duplicated stderr on task failure in replacing mode Feb 21, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where error messages from stderr were duplicated when tasks failed in the 'replacing' output mode. The core change involves distinguishing between stdout and stderr lines when accumulating output, ensuring that only stdout, which was previously hidden by progress indicators, is replayed upon task failure, thereby providing a cleaner and more accurate error presentation.

Highlights

  • Eliminated duplicate stderr output: When a task failed in --output replacing mode, stderr was printed twice: once during execution and again by the on_error() function. This has been resolved.
  • Output source tagging: The combined_output now stores each line along with a boolean indicating if it originated from stdout or stderr, allowing for selective replaying.
  • Refined error output handling: The on_error() function was updated to only replay stdout lines, as stderr lines were already visible during execution, preventing redundancy.
Changelog
  • src/cmd.rs
    • Modified the combined_output vector to store tuples of (String, bool) to differentiate between stdout and stderr lines.
    • Updated calls to on_error to pass the new Vec<(String, bool)> type instead of a single String.
    • Refactored the on_error function to filter and only print stdout lines when replaying output on task failure, preventing stderr duplication.
Activity
  • Verified that --output replacing no longer duplicates stderr on task failure.
  • Confirmed that stdout is still displayed on failure, as it was previously hidden by the progress indicator.
  • Ensured that all other output modes (default, prefix, interleave, raw, quiet, silent, timed, keep-order) remain unaffected.
  • Confirmed that successful tasks are unaffected by these changes.
  • All 481 unit tests passed, indicating no regressions.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@jdx jdx force-pushed the fix/task-failure-output-duplication branch from 8ed0258 to 10cdab4 Compare February 21, 2026 14:50
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request addresses a bug where stderr output was duplicated when a task failed in --output replacing mode. The fix involves tagging output lines as either stdout or stderr and replaying only stdout lines on error, as stderr was already printed during execution. This is a good fix that improves the user experience by preventing redundant output. The changes are well-tested and consider various output modes and success/failure scenarios.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug where stderr output was duplicated when a task failed in --output replacing mode. The fix distinguishes between stdout and stderr in the accumulated output buffer and only replays stdout on error, since stderr was already displayed during execution.

Changes:

  • Modified output accumulation to track whether each line came from stdout or stderr using a tuple (String, bool)
  • Updated on_error() to filter and replay only stdout lines, preventing stderr duplication
  • Updated all on_error() call sites to pass the new Vec<(String, bool)> type instead of String

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/cmd.rs Outdated
let line = self.redactor.redact(&line);
self.on_stdout(line.clone());
combined_output.push(line);
combined_output.push((line, true));
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

Consider using a more descriptive tuple field name or a struct to improve code readability. The boolean flag 'true' for stdout and 'false' for stderr is not immediately clear from the code. A struct like struct OutputLine { content: String, is_stdout: bool } or using a named tuple pattern would make the intent clearer and reduce the risk of confusion.

Copilot uses AI. Check for mistakes.
When a task fails in replacing output mode, on_error() re-printed all
accumulated output (stdout + stderr) via pr.println(). But stderr lines
were already printed during execution via pr.println() in on_stderr(),
causing them to appear twice.

Fix by tagging each output line with its source (stdout vs stderr) and
only replaying stdout lines in on_error(). Stdout needs replay because
it was only shown transiently via pr.set_message() (progress indicator),
while stderr was already permanently printed.

Also update CLAUDE.md to clarify that task-related changes should use
the `task` scope, not `run`.

Fixes #8267

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the fix/task-failure-output-duplication branch from 10cdab4 to 5b1339e Compare February 21, 2026 14:53
@jdx jdx enabled auto-merge (squash) February 21, 2026 14:59
@jdx jdx merged commit b78abf5 into main Feb 21, 2026
35 checks passed
@jdx jdx deleted the fix/task-failure-output-duplication branch February 21, 2026 15:10
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 x -- echo 22.9 ± 1.1 21.8 35.0 1.00
mise x -- echo 23.0 ± 1.6 21.6 38.8 1.01 ± 0.08

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 env 22.0 ± 1.2 21.1 35.6 1.00
mise env 22.5 ± 1.2 21.1 26.4 1.02 ± 0.08

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 hook-env 22.7 ± 1.1 21.6 39.5 1.00
mise hook-env 22.8 ± 0.5 22.0 25.1 1.01 ± 0.05

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 ls 19.8 ± 0.3 19.2 22.0 1.00
mise ls 19.9 ± 0.2 19.4 21.1 1.00 ± 0.02

xtasks/test/perf

Command mise-2026.2.17 mise Variance
install (cached) 123ms 124ms +0%
ls (cached) 75ms 75ms +0%
bin-paths (cached) 79ms 79ms +0%
task-ls (cached) 813ms 800ms +1%

jdx pushed a commit that referenced this pull request Feb 21, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [#8277](#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[#8288](#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [#8276](#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [#8290](#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[#8292](#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [#8285](#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [#8275](#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [#8279](#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [#8274](#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [#8286](#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [#8291](#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [#8282](#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[#8273](#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [#8283](#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[#8289](#8289)

### New Contributors

- @altendky made their first contribution in
[#8290](#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Feb 22, 2026
## [2026.2.18](https://github.com/jdx/mise/compare/v2026.2.17..v2026.2.18) - 2026-02-21

### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx in [#8277](jdx/mise#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in [#8288](jdx/mise#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion by @jdx in [#8276](jdx/mise#8276)
- **(install)** validate --locked before --dry-run short-circuit by @altendky in [#8290](jdx/mise#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in [#8292](jdx/mise#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties by @jdx in [#8285](jdx/mise#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode by @jdx in [#8275](jdx/mise#8275)
- **(task)** use process groups to kill child process trees on Unix by @jdx in [#8279](jdx/mise#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx in [#8274](jdx/mise#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by @jdx in [#8286](jdx/mise#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by @jdx in [#8291](jdx/mise#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx in [#8282](jdx/mise#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in [#8273](jdx/mise#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself by @jdx in [#8283](jdx/mise#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in [#8289](jdx/mise#8289)

### New Contributors

- @altendky made their first contribution in [#8290](jdx/mise#8290)

### 📦 Aqua Registry Updates

#### New Packages (4)

- [`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
- [`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
- [`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

- [`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)

## [2026.2.17](https://github.com/jdx/mise/compare/v2026.2.16..v2026.2.17) - 2026-02-19

### 🚀 Features

- **(prepare)** update mtime of outputs after command is run by @halms in [#8243](jdx/mise#8243)

### 🐛 Bug Fixes

- **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [#8234](jdx/mise#8234)
- **(use)** write to config.toml instead of config.local.toml by @jdx in [#8240](jdx/mise#8240)
- default legacy .mise.backend installs to non-explicit by @jean-humann in [#8245](jdx/mise#8245)

### 🚜 Refactor

- **(config)** consolidate flat task_* settings into nested task.* by @jdx in [#8239](jdx/mise#8239)

### Chore

- **(prepare)** refactor common code into ProviderBase by @halms in [#8246](jdx/mise#8246)

### 📦 Aqua Registry Updates

#### Updated Packages (1)

- [`namespacelabs/foundation/nsc`](https://github.com/namespacelabs/foundation/nsc)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
…dx#8275)

## Summary
- When a task fails in `--output replacing` mode, `on_error()`
re-printed all accumulated output (stdout + stderr) via `pr.println()`,
but stderr was already printed during execution, causing duplication
- Fix by tagging each output line with its source (stdout vs stderr) and
only replaying stdout lines in `on_error()`, since stdout was hidden
behind the progress indicator (`pr.set_message()`) while stderr was
already permanently visible

Fixes jdx#8267

## Test plan
- [x] Verified `--output replacing` no longer duplicates stderr on task
failure
- [x] Verified stdout is still shown on failure (it was hidden behind
progress indicator)
- [x] Verified all other output modes (default, prefix, interleave, raw,
quiet, silent, timed, keep-order) are unaffected
- [x] Verified successful tasks are unaffected
- [x] All 481 unit tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small, localized change to command output handling on failures; main
risk is inadvertently suppressing or mis-ordering error output in edge
cases.
> 
> **Overview**
> Fixes a failure-mode logging bug where `--output replacing` could
duplicate stderr by replaying all captured output on error.
> 
> `CmdLineRunner` now tags captured lines with an `OutputSource` and
updates `on_error` to replay **only stdout** (and only when stdout was
hidden behind the progress indicator), leaving stderr to be shown just
once during execution. Also updates the raw-exec error path to pass an
empty output vector and tweaks contributor guidance in `CLAUDE.md` to
prefer the `task` scope for task-related changes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5b1339e. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [jdx#8277](jdx#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[jdx#8288](jdx#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [jdx#8276](jdx#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [jdx#8290](jdx#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[jdx#8292](jdx#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [jdx#8285](jdx#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [jdx#8275](jdx#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [jdx#8279](jdx#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [jdx#8274](jdx#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [jdx#8286](jdx#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [jdx#8291](jdx#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [jdx#8282](jdx#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[jdx#8273](jdx#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [jdx#8283](jdx#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[jdx#8289](jdx#8289)

### New Contributors

- @altendky made their first contribution in
[jdx#8290](jdx#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
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.

2 participants