runCommand vs runCommandChecked in OpenAI Codex Plugin: Key Differences Explained
runCommand returns a result object regardless of exit status, while runCommandChecked throws an exception on failure, providing automatic error handling for shell commands in the Codex plugin.
The OpenAI Codex plugin (openai/codex-plugin-cc) provides two distinct Node.js utilities for executing shell commands synchronously. Understanding the difference between runCommand and runCommandChecked is essential for writing robust plugin scripts, as one returns raw results for manual inspection while the other enforces success through exceptions.
Source Location and Implementation
Both functions are defined in plugins/codex/scripts/lib/process.mjs and serve as thin wrappers around Node.js's spawnSync. The runCommand function acts as the low-level primitive, while runCommandChecked builds upon it to add automatic error validation.
runCommand Implementation
The runCommand function (lines 4-25) always returns a plain result object containing the command output and exit status:
export function runCommand(command, args = [], options = {}) {
// ... spawns the process synchronously
return { command, args, status, signal, stdout, stderr, error };
}
runCommandChecked Implementation
The runCommandChecked function (lines 27-36) delegates to runCommand then validates the result:
export function runCommandChecked(command, args = [], options = {}) {
const result = runCommand(command, args, options);
if (result.error) throw result.error;
if (result.status !== 0) throw new Error(formatCommandFailure(result));
return result;
}
Core Differences Between runCommand and runCommandChecked
| Feature | runCommand |
runCommandChecked |
|---|---|---|
| Return behavior | Always returns result object { command, args, status, signal, stdout, stderr, error } |
Returns result object only on success |
| Error handling | Caller must manually check result.error or result.status !== 0 |
Automatically throws on spawn errors or non-zero exit codes |
| Typical use case | Commands where non-zero exits are expected or custom diagnostics are needed | Commands that must succeed for the script to continue |
Real-World Usage in git.mjs
The plugins/codex/scripts/lib/git.mjs file demonstrates practical application of both approaches. At line 13, runCommand handles git rev-parse where the caller needs to inspect raw output regardless of success:
const result = runCommand("git", args, { cwd, ... });
At line 17, runCommandChecked wraps git fetch to ensure the operation succeeds or throws immediately:
runCommandChecked("git", args, { cwd, ... });
Practical Code Examples
Manual Error Handling with runCommand
Use runCommand when you need to parse output or handle specific exit codes gracefully:
import { runCommand } from "./process.mjs";
const result = runCommand("git", ["rev-parse", "HEAD"], { cwd: repoRoot });
if (result.error) {
console.error("Git failed to start:", result.error);
} else if (result.status !== 0) {
console.warn("Git exited with non-zero code:", result.stderr);
} else {
console.log("Current commit hash:", result.stdout.trim());
}
Automatic Error Propagation with runCommandChecked
Use runCommandChecked for "fire-and-forget" operations that should abort execution on failure:
import { runCommandChecked } from "./process.mjs";
try {
const { stdout } = runCommandChecked("git", ["fetch"], { cwd: repoRoot });
console.log("Fetch succeeded:", stdout);
} catch (e) {
console.error("Failed to fetch:", e.message);
}
Combining Both Approaches
Higher-level helpers can mix both functions to handle optional and mandatory operations:
import { runCommand, runCommandChecked } from "./process.mjs";
export function ensureRemoteExists(name, url, cwd) {
// Check if remote exists - non-fatal
const list = runCommand("git", ["remote", "-v"], { cwd });
if (list.stdout.includes(name)) return;
// Add remote - must succeed
runCommandChecked("git", ["remote", "add", name, url], { cwd });
}
Summary
- runCommand in
plugins/codex/scripts/lib/process.mjsreturns a complete result object for every invocation, placing error-handling responsibility on the caller to inspectresult.statusorresult.error. - runCommandChecked wraps
runCommandto provide automatic exception throwing when commands fail to spawn or exit with non-zero status codes, usingformatCommandFailurefor descriptive messages. - Choose runCommand when you need to inspect exit codes, parse error output, or handle expected failures gracefully according to the OpenAI Codex plugin source code.
- Choose runCommandChecked when a command must succeed for the script to continue, keeping calling code clean through exception handling rather than manual status checks.
Frequently Asked Questions
What does runCommand return when a command fails?
When a command fails, runCommand still returns a result object containing the error property (if spawn failed) or a non-zero status code. The function never throws, allowing callers to inspect result.status !== 0 or result.error to determine failure type and handle it appropriately.
How does runCommandChecked format error messages?
runCommandChecked uses the internal formatCommandFailure helper function to build descriptive error messages when commands exit with non-zero status. For spawn failures, it throws the original result.error directly rather than wrapping it in a new Error instance.
Can I disable the automatic throwing in runCommandChecked?
No, runCommandChecked is designed specifically to always throw on failure. If you need conditional error handling or need to inspect non-zero exit codes, use runCommand instead and implement custom logic based on the returned status and stderr properties according to the implementation in process.mjs.
Are these functions used elsewhere in the OpenAI Codex plugin?
Yes, beyond plugins/codex/scripts/lib/git.mjs, these utilities appear throughout the plugin codebase wherever shell command execution is required, including test suites like tests/process.test.mjs which verify both the raw result handling and error propagation behaviors.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →