tj/commander.js · critical · Error
'${executableFile}' not executable
Error message
'${executableFile}' not executable What it means
Thrown by the child_process error handler in Command._executeSubCommand() at lib/command.js:1339-1342 when spawning an executable subcommand fails with EACCES — the resolved executable file exists but lacks the executable bit (chmod +x). Distinct from ENOENT (missing file, see error[16]): here the file is found but the OS refuses to run it.
Source
Thrown at lib/command.js:1341
new CommanderError(
code,
'commander.executeSubCommandAsync',
'(close)',
),
);
}
});
proc.on('error', (err) => {
// @ts-ignore: because err.code is an unknown property
if (err.code === 'ENOENT') {
this._checkForMissingExecutable(
executableFile,
executableDir,
subcommand._name,
);
// @ts-ignore: because err.code is an unknown property
} else if (err.code === 'EACCES') {
throw new Error(`'${executableFile}' not executable`);
}
if (!exitCallback) {
process.exit(1);
} else {
const wrappedError = new CommanderError(
1,
'commander.executeSubCommandAsync',
'(error)',
);
wrappedError.nestedError = err;
exitCallback(wrappedError);
}
});
// Store the reference to the child process
this.runningCommand = proc;
}
View on GitHub (pinned to ba6d13ddb4)
Solutions
- Set the executable bit: `chmod +x <file>` (e.g. `chmod +x bin/mycli-pull.js`).
- Ensure your shebang (`#!/usr/bin/env node`) is present so the OS knows how to run it once executable.
- If deploying via npm, declare the file in package.json `bin` so npm sets +x on install, and verify the `files` array includes it.
- In Docker/CI, copy with `COPY --chmod=0755` or run chmod in the image build.
Example fix
# before: file is -rw-r--r-- # bin/mycli-pull.js # after chmod +x bin/mycli-pull.js # ensure shebang on first line: #!/usr/bin/env node
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
function assertExecutable(file) {
if (!fs.existsSync(file)) throw new Error(`missing: ${file}`);
const st = fs.statSync(file);
if (!(st.mode & 0o111)) throw new Error(`${file} is not executable (run chmod +x)`);
} Try / catch
try { await program.parseAsync(); }
catch (e) {
if (/not executable/i.test(e.message)) {
console.error('Run: chmod +x <subcommand-file>');
process.exit(126);
}
throw e;
} Prevention
- Always `chmod +x` executable subcommand scripts and include a shebang.
- Declare them in package.json `bin` so npm sets the bit on install.
- In Docker/CI use COPY --chmod=0755 or a chmod step; verify on Windows checkouts.
When it happens
Trigger: An executable subcommand file exists but has mode 0644 (no execute permission). Common after `git clone` on Windows where the mode bit was lost, or when files were created via a tool that didn't preserve +x. The spawn raises EACCES and the handler re-throws.
Common situations: Cross-platform development (Windows doesn't track the bit; deploy to Linux loses it); files written by a bundler that resets permissions; npm packaging that strips the bit; container images copied with non-preserving cp.
Related errors
- '${executableFile}' does not exist - if '${subcommandName}'
- Not a number.
- Not a number.
- Allowed choices are ${this.argChoices.join(', ')}.
- Command passed to .addCommand() must have a name - specify t
AI-assisted analysis of tj/commander.js@ba6d13ddb4 (2026-08-03).
Data as JSON: /data/errors/09867dea11a4f6c7.json.
Report an issue: GitHub.