affaan-m/ECC · error · Error
Refusing to invoke an Itô CLI shim. Set
Error message
Refusing to invoke an Itô CLI shim. Set ${EXECUTABLE_OVERRIDE} to the absolute dist/bin/ito.js path. What it means
buildInvocation re-runs isCanonicalItoEntry on the executable before constructing the spawn, as a defense-in-depth check: ECC invokes the CLI as `process.execPath <executable> ...args` and refuses to pass a non-canonical entry (for example an `ito` shim resolved from PATH) any arguments, because arguments may carry credentials. In normal flow this is unreachable — resolveItoExecutable already returns a verified canonical path — so hitting it means invokeIto/buildInvocation was called directly with an arbitrary executable string.
Solutions
- Always derive the executable from resolveItoExecutable(environment) and pass that exact value through to invokeIto.
- Set ECC_ITO_CLI_EXECUTABLE to the canonical dist/bin/ito.js path so resolution succeeds normally.
- In tests, create a fake path whose final segments are dist/bin/ito.js so isCanonicalItoEntry passes.
Example fix
// before
import { invokeIto } from '../scripts/ito.js';
invokeIto('/usr/local/bin/ito', ['status']); // shim -> throws
// after
import { invokeIto, resolveItoExecutable } from '../scripts/ito.js';
invokeIto(resolveItoExecutable(process.env), ['status']); Defensive patterns
Strategy: validation
Validate before calling
// Never hand-pick an executable string; always flow the resolved value through.
import { resolveItoExecutable, invokeIto } from './ito.js';
const exe = resolveItoExecutable(process.env); // already passes all checks
invokeIto(exe, ['status']); // buildInvocation's re-check now succeeds Try / catch
try { invokeIto(exe, args); } catch (e) {
if (/Refusing to invoke an Itô CLI shim/.test(e.message)) { throw new Error('Caller bug: executable must come from resolveItoExecutable()'); }
throw e;
} Prevention
- Treat resolveItoExecutable() as the single source of truth for the executable; do not re-resolve via PATH or which.
- In tests, fake executables whose path ends with cli/ito-compute-cli/dist/bin/ito.js so the canonical check passes.
When it happens
Trigger: Another script imports scripts/ito.js internals and calls invokeIto('/usr/local/bin/ito', args) or buildInvocation with a PATH-resolved shim instead of the resolveItoExecutable() return value.
Common situations: Embedding the ECC ito wrapper as a library; refactors that bypass resolveItoExecutable; tests stubbing the executable path with a mock file.
Related errors
- ECC_ITO_CLI_EXECUTABLE must point to the canonical…
- Path ' ' targets a system directory
- Refusing to invoke an Itô CLI shim. Set…
- The canonical ito-compute-cli is unpublished and ECC will…
- Agents directory not found
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/3aea20625b320bda.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/ito.js:257
? segment.toLowerCase() === expected.toLowerCase()
: segment === expected;
});
}
function isUsableExecutable(candidate) {
try {
const info = fs.statSync(candidate);
if (!info.isFile()) return false;
fs.accessSync(candidate, fs.constants.R_OK);
return true;
} catch {
return false;
}
}
function buildInvocation(executable, args) {
if (!isCanonicalItoEntry(executable)) {
throw new Error(
`Refusing to invoke an Itô CLI shim. Set ${EXECUTABLE_OVERRIDE} to the absolute dist/bin/ito.js path.`
);
}
return Object.freeze({
executable: process.execPath,
args: Object.freeze([executable, ...args]),
});
}
function invokeIto(executable, args, environment = process.env) {
const invocation = buildInvocation(executable, args);
const command = getInvocationCommand(args);
const isNodeQualification = command === "evals";
const isDeviceLogin = command === "login";
const result = spawnSync(invocation.executable, invocation.args, {
cwd: process.cwd(),
encoding: "utf8",
// Keep policy helpers immutable for callers, but give child-processView on GitHub (pinned to 06c5e118c4)