affaan-m/ECC · error · Error

typing requires a file path

Error message

typing requires a file path

What it means

cmdTyping shows a typing/activity indicator in the canvas chat for the session associated with a plan file. The file path is required to compute the session key; when the positional argument is absent the command throws 'typing requires a file path' before any server contact. The intent is that typing signals are best-effort, but the CLI still refuses to run without knowing which session to signal.

Solutions

  1. Invoke as `ecc-plan-canvas typing <file> [--state typing|thinking|idle]`, passing the same path used with `open`.
  2. In scripts, assert the path variable is set before calling the command.
  3. If the wrapper drops arguments, pass them after `--` or fix the wrapper's argument forwarding.
  4. Remember typing is fire-and-forget: only the missing-file case throws here; once a path is supplied a dead server just yields status 'no-server'.

Example fix

// before (agent script)
exec(`ecc-plan-canvas typing --state ${state}`);

// after
if (!planPath) throw new Error('planPath required for typing signal');
exec(`ecc-plan-canvas typing ${JSON.stringify(planPath)} --state ${state}`);
Defensive patterns

Strategy: validation

Validate before calling

if (!file) throw new Error('typing requires a file path');
if (!fs.existsSync(file)) throw new Error(`plan file not found: ${file}`);

Prevention

When it happens

Trigger: Calling `ecc-plan-canvas typing` without a file positional — omitting the argument entirely, or an agent harness invoking the subcommand with only `--state thinking` and no path.

Common situations: Automating presence indicators from a script where the plan path lives in a variable that is empty; calling the subcommand from the wrong working directory and assuming it infers the file; mixing up argument order so the path lands where a flag expects it and is consumed.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/9912c83e6ca0736f. Report an issue: GitHub.

Appendix: source

Thrown at scripts/plan-canvas.js:302

  process.stderr.write('[plan-canvas] waiting for human feedback... leave this running (re-run if interrupted; queued feedback is never lost)\n');
  const result = await awaitRequest(port, sessionKeyFor(canonicalizeArtifactPath(file)), timeoutMs);
  if (result.status === 'feedback') {
    result.next_step = result.sessionEnded
      ? 'The user sent this feedback and ended the session. Address it and report in chat; do not reopen the canvas uninvited.'
      : 'Address the feedback, then run `ecc-plan-canvas await <file> --reply "<what you changed>"` to answer in the canvas and keep listening.';
  } else if (result.status === 'ended') {
    result.next_step =
      result.endedBy === 'user'
        ? 'The user ended this review. Stop polling and deliver any remaining updates in chat; do not reopen uninvited.'
        : 'Session ended. Stop polling.';
  }
  return result;
}

// Show the human an activity indicator in the canvas chat. Cheap and
// fire-and-forget: a failed signal must never derail the actual work.
async function cmdTyping(file, args, { port }) {
  if (!file) throw new Error('typing requires a file path');
  const state = valueAfter(args, '--state') || 'typing';
  if (!(await healthCheck(port))) return { status: 'no-server' };
  const key = sessionKeyFor(canonicalizeArtifactPath(file));
  const res = await request(port, 'POST', `/api/session/${key}/typing`, { state });
  if (res.statusCode !== 200) throw new Error(res.body.error || `typing failed (HTTP ${res.statusCode})`);
  return { status: 'ok', state, presence: res.body.presence };
}

// Report feedback the human sent that no agent has picked up yet. Reads state
// directly so it answers even when the server has idled out.
function cmdPending({ stateDir }) {
  const store = createSessionStore({ stateDir });
  const waiting = store
    .list()
    .filter(session => session.status !== 'ended' && session.pending > 0)
    .map(session => ({ file: session.file, pending: session.pending, updatedAt: session.updatedAt }));
  return {
    status: waiting.length ? 'pending' : 'clear',

View on GitHub (pinned to 8321021c54)