Skip to content

fix(task): improve error message when task files are not executable - #8705

Merged
jdx merged 5 commits into
mainfrom
fix/non-executable-task-error-message
Mar 22, 2026
Merged

jdx merged 5 commits into
mainfrom
fix/non-executable-task-error-message

Conversation

@jdx

@jdx jdx commented Mar 22, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • When task files exist in mise-tasks/ but lack the executable bit (common when created on Windows and cloned on macOS/Linux), show a clear error with chmod +x instructions instead of the generic "no tasks defined" message
  • Added warning to mise tasks ls when no tasks found but non-executable files exist in task directories
  • Added e2e test covering both mise run and mise tasks cases

Closes #8614

Test plan

  • mise run build with non-executable task files shows "non-executable" error with chmod +x suggestion
  • mise tasks with non-executable task files shows warning about non-executable files
  • After chmod +x, both mise tasks and mise run work correctly
  • e2e test test_task_non_executable_error passes

🤖 Generated with Claude Code


Note

Low Risk
Low risk: changes are limited to error/warning messaging when no tasks are detected (non-Windows) plus a new e2e test; core task execution logic is unchanged.

Overview
Improves the "no tasks" failure path by detecting non-executable files in task include directories and surfacing a clearer error suggesting chmod +x (instead of the generic "no tasks defined" message).

Adds a warning in mise tasks ls when no tasks exist at all but non-executable task files are present (to avoid false positives from flag filtering), and introduces an e2e test verifying both the warning/error and the success case after making files executable.

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

When task files exist in mise-tasks/ but lack the executable bit (common
when created on Windows), show a clear error with chmod +x instructions
instead of the generic "no tasks defined" message.

Closes #8614

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 significantly enhances the user experience for mise task management by providing more informative feedback when task files are not executable. Previously, users might encounter a generic "no tasks defined" error; now, the system intelligently detects non-executable task files and offers clear guidance, including chmod +x instructions, to resolve the issue. This change addresses a common problem, especially for users transferring files across different operating systems, and improves the discoverability and usability of tasks.

Highlights

  • Improved Error Messaging: When mise run attempts to execute a task from a file that exists but lacks executable permissions, a clear error message is now displayed, including instructions to use chmod +x.
  • Non-Executable File Warnings: The mise tasks ls command now issues a warning if no tasks are found but non-executable files are detected within the configured task directories, guiding users to make them executable.
  • New End-to-End Test: An end-to-end test has been added to validate the new error messages and warnings for non-executable task files in both mise run and mise tasks scenarios.
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.

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. ↩

@greptile-apps

greptile-apps Bot commented Mar 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR improves the UX for a common Windows→macOS/Linux workflow issue: when task files exist in mise-tasks/ but lack the executable bit, mise now surfaces a clear error/warning with an explicit chmod +x suggestion instead of the opaque "no tasks defined" message.

Key changes:

  • New shared helper find_non_executable_task_files extracted into task_list.rs, eliminating the duplicate walkdir traversal that existed in a prior iteration of this PR.
  • err_no_task in task_list.rs gains a non-executable-file check (non-Windows only) that bails with a formatted error listing the count, directories, and up to 5 example paths.
  • tasks ls in ls.rs now splits tasks into all_tasks (unfiltered) and tasks (after --global/--local/--hidden filters), so the non-executable warning only fires when there are truly zero tasks — not when a filter simply yields an empty subset.
  • e2e test covers the complete lifecycle and validates both mise run and mise tasks output for "non-executable" and "chmod +x" strings.

Minor remaining polish item (not blocking): the space-joined file paths in the chmod +x example in task_list.rs are not quoted, so paths with spaces would produce a misleading shell command.

Confidence Score: 4/5

  • Safe to merge — changes are confined to error/warning paths and an e2e test; no effect on normal task execution or discovery.
  • The major concerns from previous review rounds (false-positive filter warning, duplicated walkdir logic, missing chmod +x in the e2e mise tasks assertion) have all been addressed. One small new style issue (unquoted paths in the chmod +x suggestion) remains, but it only affects the display of the error message in the unlikely event a task file name contains a space. No correctness or data-loss risk.
  • No files require special attention; the path-quoting issue in src/task/task_list.rs is the only remaining item and is non-blocking.

Important Files Changed

Filename Overview
src/task/task_list.rs Adds find_non_executable_task_files shared helper and integrates it into err_no_task to produce an actionable chmod +x error; a few previously-flagged style issues (dirs_with_files scope, redundant CWD clone) remain unaddressed but don't affect correctness.
src/cli/tasks/ls.rs Splits tasks into all_tasks/tasks to fix the false-positive warning when filters like --global/--local produce an empty result, and reuses the shared find_non_executable_task_files helper; warning message still omits specific file paths (flagged in a prior thread).
e2e/tasks/test_task_non_executable_error New e2e test covers the full lifecycle (non-executable → helpful error → chmod → tasks work) and now checks for both "non-executable" and "chmod +x" strings in both mise run and mise tasks output.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User runs mise run &lt;task&gt; or mise tasks] --> B{Tasks found?}
    B -- Yes --> C[Execute / list tasks normally]
    B -- No / empty --> D{Platform Windows?}
    D -- Yes --> E[Generic error: no tasks defined]
    D -- No --> F[find_non_executable_task_files]
    F --> G{Non-executable files exist in include dirs?}
    G -- No --> E
    G -- Yes --> H{Called from err_no_task?}
    H -- Yes, mise run --> I[bail! with count + dir list + chmod +x suggestion]
    H -- No, mise tasks ls --> J[warn! about non-executable files + chmod +x hint]
Loading

Fix All in Claude Code

Reviews (5): Last reviewed commit: "fix(task): allow hidden root dirs like ...." | Re-trigger Greptile

Comment thread src/cli/tasks/ls.rs Outdated
Comment thread src/task/task_list.rs Outdated
Comment thread src/task/task_list.rs Outdated

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request improves the user experience by providing a more informative error message when task files are not executable, which is a common issue for users moving between Windows and Unix-like systems. The changes include a new e2e test to cover this scenario, a warning in mise tasks ls, and a detailed error in mise run. The implementation is solid, but I've suggested a small refactoring to improve performance and code clarity in one of the new code blocks.

Comment thread src/task/task_list.rs Outdated
Comment on lines +130 to +162
let non_exec_files: Vec<PathBuf> = includes
.iter()
.filter(|d| d.is_dir())
.flat_map(|d| {
walkdir::WalkDir::new(d)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.map(|e| e.path().to_path_buf())
.filter(|p| !file::is_executable(p))
.collect::<Vec<_>>()
})
.collect();
if !non_exec_files.is_empty() {
let dirs_with_files: Vec<String> = includes
.iter()
.filter(|d| d.is_dir())
.map(|d| display_path(d))
.collect();
bail!(
"no tasks defined in {}, but found {} non-executable file(s) in {}.\n\
Files must be executable to be detected as tasks.\n\
Run `chmod +x` on the task files to fix this, e.g.:\n chmod +x {}",
display_path(dirs::CWD.clone().unwrap_or_default()),
non_exec_files.len(),
dirs_with_files.join(", "),
non_exec_files
.iter()
.take(5)
.map(|p| display_path(p))
.collect::<Vec<_>>()
.join(" "),
);

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.

medium

This logic for finding non-executable files can be made more efficient and readable.

  1. The iterator chain for finding non-executable files collects into a Vec for each subdirectory inside the flat_map, which is inefficient. This can be avoided by chaining iterators directly and letting the final collect do the work.
  2. The bail! macro can use the cwd variable that is already in scope, which is a minor cleanup.

I've combined these improvements in the suggestion below.

Additionally, this logic is very similar to the one in src/cli/tasks/ls.rs. Consider extracting it into a shared helper function in a future refactoring to avoid code duplication.

                let non_exec_files: Vec<PathBuf> = includes
                    .iter()
                    .filter(|d| d.is_dir())
                    .flat_map(|d| {
                        walkdir::WalkDir::new(d)
                            .into_iter()
                            .filter_map(|e| e.ok())
                            .filter(|e| e.file_type().is_file() && !file::is_executable(e.path()))
                            .map(|e| e.path().to_path_buf())
                    })
                    .collect();
                if !non_exec_files.is_empty() {
                    let dirs_with_files: Vec<String> = includes
                        .iter()
                        .filter(|d| d.is_dir())
                        .map(|d| display_path(d))
                        .collect();
                    bail!(
                        "no tasks defined in {}, but found {} non-executable file(s) in {}.\n\
                        Files must be executable to be detected as tasks.\n\
                        Run `chmod +x` on the task files to fix this, e.g.:\n  chmod +x {}",
                        display_path(cwd),
                        non_exec_files.len(),
                        dirs_with_files.join(", "),
                        non_exec_files
                            .iter()
                            .take(5)
                            .map(|p| display_path(p))
                            .collect::<Vec<_>>()
                            .join(" "),
                    );

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/cli/tasks/ls.rs
Comment thread src/task/task_list.rs
Comment thread src/task/task_list.rs Outdated
Comment thread src/cli/tasks/ls.rs
Comment thread src/cli/tasks/ls.rs Outdated
- Skip hidden files (e.g., .gitkeep, .DS_Store) when scanning for
  non-executable task files, matching the behavior of load_tasks_includes
- Check unfiltered task count in `mise tasks ls` so the warning doesn't
  fire when tasks exist but are filtered by --hidden/--local/--global

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jdx
jdx force-pushed the fix/non-executable-task-error-message branch from cf42dcf to 2523b69 Compare March 22, 2026 14:58
Comment thread e2e/tasks/test_task_non_executable_error

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread src/task/task_list.rs Outdated
The filter_entry was skipping all entries starting with '.', including
the WalkDir root itself. This meant .mise-tasks/ contents were never
scanned. Match load_tasks_includes behavior by exempting the root.

Also add chmod +x assertion to mise tasks warning test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/task/task_list.rs
Comment on lines +167 to +172
non_exec_files
.iter()
.take(5)
.map(display_path)
.collect::<Vec<_>>()
.join(" "),

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.

P2 Unquoted paths in chmod +x example

File paths are joined with a plain space: if any task file has a space in its name (e.g. mise-tasks/my build), the generated chmod +x command would be ambiguous and incorrect at the shell. Consider wrapping each path in double-quotes:

Suggested change
non_exec_files
.iter()
.take(5)
.map(display_path)
.collect::<Vec<_>>()
.join(" "),
non_exec_files
.iter()
.take(5)
.map(|p| format!("\"{}\"", display_path(p)))
.collect::<Vec<_>>()
.join(" "),

Fix in Claude Code

@jdx
jdx enabled auto-merge (squash) March 22, 2026 15:18
@jdx
jdx merged commit a6f287f into main Mar 22, 2026
36 checks passed
@jdx
jdx deleted the fix/non-executable-task-error-message branch March 22, 2026 15:22
@github-actions

Copy link
Copy Markdown
Contributor

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.10 x -- echo 18.9 ± 0.8 17.5 22.1 1.00
mise x -- echo 18.9 ± 0.6 17.7 24.2 1.00 ± 0.05

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.10 env 18.3 ± 0.7 17.1 23.2 1.00
mise env 19.5 ± 0.7 17.8 22.6 1.06 ± 0.06

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.10 hook-env 20.7 ± 0.3 19.7 23.8 1.00
mise hook-env 21.3 ± 2.1 20.4 59.0 1.03 ± 0.10

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.10 ls 19.4 ± 0.7 18.0 21.4 1.00 ± 0.05
mise ls 19.4 ± 0.6 17.8 21.5 1.00

xtasks/test/perf

Command mise-2026.3.10 mise Variance
install (cached) 116ms 114ms +1%
ls (cached) 69ms 69ms +0%
bin-paths (cached) 69ms 69ms +0%
task-ls (cached) 746ms 718ms +3%

mise-en-dev added a commit that referenced this pull request Mar 22, 2026
### 🚀 Features

- **(github)** read tokens from gh CLI hosts.yml config by @jdx in
[#8692](#8692)
- **(task)** support optional `args` and `env` fields in `run` entries
by @jdx in [#8687](#8687)
- **(task)** add --skip-tools flag to mise run by @jdx in
[#8699](#8699)
- **(vfox)** add try_get, try_head, try_download_file to Lua HTTP module
by @jdx in [#8697](#8697)

### 🐛 Bug Fixes

- **(config)** recognize SSH and other non-HTTPS URLs in get_repo_url by
@modestman in [#8666](#8666)
- **(docs)** add dark mode support to favicon by @jdx in
[#8678](#8678)
- **(env)** support multiple --env/-E flags by @jdx in
[#8686](#8686)
- **(github)** rename_exe renames correct binary when archive contains
multiple executables by @jdx in
[#8700](#8700)
- **(implode)** include system data dir in implode cleanup by @jdx in
[#8696](#8696)
- **(install)** skip GitHub API calls for aqua tools in --locked mode by
@jdx in [#8679](#8679)
- **(install)** skip redundant provenance verification when lockfile has
integrity data by @jdx in [#8688](#8688)
- **(lock)** respect existing platforms in lockfile when running `mise
lock` by @jdx in [#8708](#8708)
- **(lock)** skip global config lockfile by default by @jdx in
[#8707](#8707)
- **(node)** expand tilde in default_packages_file path by @jdx in
[#8709](#8709)
- **(shell)** error when no version specified instead of silent no-op by
@jdx in [#8693](#8693)
- **(shim)** detect shims by checking shims directory instead of binary
name by @jdx in [#8694](#8694)
- **(task)** inherit task_config.dir for included TOML and file tasks by
@jdx in [#8689](#8689)
- **(task)** strip inline args when validating run.tasks references by
@jdx in [#8701](#8701)
- **(task)** include idiomatic version files in monorepo task toolset by
@jdx in [#8702](#8702)
- **(task)** improve error message when task files are not executable by
@jdx in [#8705](#8705)
- **(test)** update vfox provenance test for checksum-backed skip by
@jdx in [#8703](#8703)
- improve usage spec element support in tasks by @nkakouros in
[#8623](#8623)
- make env plugin (Module) vars available in Tera template context by
@victor-founder in [#8682](#8682)
- respect MISE_COLOR=0 for color_eyre error output by @jdx in
[#8690](#8690)
- add windows support for usage tool registry by @jdx in
[#8713](#8713)

### 📚 Documentation

- **(task)** clarify interactive task blocking behavior by @jdx in
[#8685](#8685)
- improve visibility of install_before setting by @jdx in
[#8712](#8712)

### 📦 Registry

- add rtk ([github:rtk-ai/rtk](https://github.com/rtk-ai/rtk)) by
@bricelalu in [#8683](#8683)

### New Contributors

- @victor-founder made their first contribution in
[#8682](#8682)
- @modestman made their first contribution in
[#8666](#8666)
- @bricelalu made their first contribution in
[#8683](#8683)
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.

1 participant