{"id":"09867dea11a4f6c7","repo":"tj/commander.js","slug":"executablefile-not-executable","errorCode":null,"errorMessage":"'${executableFile}' not executable","messagePattern":"'(.+?)' not executable","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"lib/command.js","lineNumber":1341,"sourceCode":"          new CommanderError(\n            code,\n            'commander.executeSubCommandAsync',\n            '(close)',\n          ),\n        );\n      }\n    });\n    proc.on('error', (err) => {\n      // @ts-ignore: because err.code is an unknown property\n      if (err.code === 'ENOENT') {\n        this._checkForMissingExecutable(\n          executableFile,\n          executableDir,\n          subcommand._name,\n        );\n        // @ts-ignore: because err.code is an unknown property\n      } else if (err.code === 'EACCES') {\n        throw new Error(`'${executableFile}' not executable`);\n      }\n      if (!exitCallback) {\n        process.exit(1);\n      } else {\n        const wrappedError = new CommanderError(\n          1,\n          'commander.executeSubCommandAsync',\n          '(error)',\n        );\n        wrappedError.nestedError = err;\n        exitCallback(wrappedError);\n      }\n    });\n\n    // Store the reference to the child process\n    this.runningCommand = proc;\n  }\n","sourceCodeStart":1323,"sourceCodeEnd":1359,"githubUrl":"https://github.com/tj/commander.js/blob/ba6d13ddb4243e5913367734f8c159089ffe7834/lib/command.js#L1323-L1359","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: file is -rw-r--r--\n#   bin/mycli-pull.js\n\n# after\nchmod +x bin/mycli-pull.js\n# ensure shebang on first line:\n#!/usr/bin/env node","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nfunction assertExecutable(file) {\n  if (!fs.existsSync(file)) throw new Error(`missing: ${file}`);\n  const st = fs.statSync(file);\n  if (!(st.mode & 0o111)) throw new Error(`${file} is not executable (run chmod +x)`);\n}","typeGuard":null,"tryCatchPattern":"try { await program.parseAsync(); }\ncatch (e) {\n  if (/not executable/i.test(e.message)) {\n    console.error('Run: chmod +x <subcommand-file>');\n    process.exit(126);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["commander","executable-subcommand","permissions","eacces","filesystem"],"analyzedSha":"ba6d13ddb4243e5913367734f8c159089ffe7834","analyzedAt":"2026-08-03T20:26:04.326Z","schemaVersion":2}