affaan-m/ECC · error · Error

Refusing to invoke an Itô CLI shim. Set…

Error message

Refusing to invoke an Itô CLI shim. Set ECC_ITO_CLI_EXECUTABLE to the absolute dist/bin/ito.js path.

What it means

buildInvocation is the last line of defense before the bridge spawns the Itô CLI: it re-validates that the resolved executable is the canonical dist/bin/ito.js and refuses anything else. This guards against shims or wrappers intercepting a credential-bearing client, since ECC deliberately never discovers the CLI through PATH.

Solutions

  1. Set ECC_ITO_CLI_EXECUTABLE to the absolute canonical path .../cli/ito-compute-cli/dist/bin/ito.js and remove any wrapper indirection.
  2. Apply env vars or logging around the invocation (e.g. in the shell that launches ECC) instead of wrapping the CLI binary itself.
  3. Rebuild from the canonical repo if you need customization; do not point ECC at a renamed/copied entry.
  4. Confirm no script or profile rewrites ECC_ITO_CLI_EXECUTABLE after your export (check shell rc files and CI env).

Example fix

// before
export ECC_ITO_CLI_EXECUTABLE=/usr/local/bin/ito-wrapper   # shim
// after
unset ITO_WRAPPER;
export ECC_ITO_CLI_EXECUTABLE=/opt/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito.js
Defensive patterns

Strategy: validation

Validate before calling

const override = process.env.ECC_ITO_CLI_EXECUTABLE;
if (override && !override.endsWith('/cli/ito-compute-cli/dist/bin/ito.js')) {
  throw new Error('Override must be the absolute dist/bin/ito.js path, not a shim');
}

Type guard

const isCanonicalEntry = (p) => typeof p === 'string' && /\/cli\/ito-compute-cli\/dist\/bin\/ito\.js$/.test(p);

Try / catch

try { await eccIto(['status']); } catch (e) { if (e.message.includes('Refusing to invoke an Itô CLI shim')) { console.error('Remove the wrapper and set ECC_ITO_CLI_EXECUTABLE to dist/bin/ito.js'); } else throw e; }

Prevention

When it happens

Trigger: Any ecc ito command where the executable handed to buildInvocation (via resolveItoExecutable/invocation) is not the canonical entry — e.g. the override was changed between validation and invocation, or a modified caller passes a shim path directly.

Common situations: Users wrapping the CLI in shell wrappers for logging or env injection; patched forks that relax earlier checks; CI images that substitute a downloaded binary.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/92687b081eeaebdf. 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-process

View on GitHub (pinned to 8321021c54)