affaan-m/ECC · error

Refusing to invoke an Itô CLI shim. Set ${EXECUTABLE_OVERRID

Error message

Refusing to invoke an Itô CLI shim. Set ${EXECUTABLE_OVERRIDE} to the absolute dist/bin/ito.js path.

What it means

Thrown by buildInvocation at scripts/ito.js:255-260 when the executable passed in does not satisfy isCanonicalItoEntry. This is a defense-in-depth re-check: resolveItoExecutable already validated the executable via assertUsableExecutable, so under normal main() flow this is unreachable. It exists so that any caller importing buildInvocation directly (the module exports it at ito.js:322) cannot bypass the canonical-entry requirement. The error text names ECC_ITO_CLI_EXECUTABLE explicitly because that is the only sanctioned route to a credential-bearing binary.

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 01e15490f0)

Solutions

  1. Do not pass a shim or wrapper to buildInvocation — pass the canonical dist/bin/ito.js path returned by resolveItoExecutable.
  2. If you are calling from downstream code, obtain the executable via resolveItoExecutable(environment) rather than constructing it yourself.
  3. If genuinely needing to invoke a non-canonical entry, that is explicitly unsupported; the canonical CLI is unpublished for security reasons.

Example fix

// before (downstream caller)
const { buildInvocation } = require('./scripts/ito.js');
const inv = buildInvocation('/usr/local/bin/ito-shim', ['status']);
// after
const { resolveItoExecutable, buildInvocation } = require('./scripts/ito.js');
const exe = resolveItoExecutable(process.env);   // honors ECC_ITO_CLI_EXECUTABLE
const inv = buildInvocation(exe, ['status']);
Defensive patterns

Strategy: validation

Validate before calling

const { resolveItoExecutable } = require('./scripts/ito.js');
// Downstream callers: never construct the executable path yourself.
const exe = resolveItoExecutable(process.env);
const invocation = buildInvocation(exe, args);

Prevention

When it happens

Trigger: Calling buildInvocation(executable, args) directly from a test or downstream tool with an executable path whose trailing segments are not [cli, ito-compute-cli, dist, bin, ito.js]. Not reachable from the CLI's main() under correct control flow because resolveItoExecutable already enforced the same invariant.

Common situations: A downstream script imports { buildInvocation } from scripts/ito.js and passes a shim, wrapper, or renamed entry. Effectively impossible to hit through `ecc ito` itself unless resolveItoExecutable or assertUsableExecutable is later refactored to return a non-canonical path.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/3aea20625b320bda. Report an issue: GitHub.