affaan-m/ECC · error · Error

end requires a file path

Error message

end requires a file path

What it means

cmdEnd terminates the canvas session tied to a plan file by POSTing the resolved path to /api/end. The file argument is mandatory; without it the command cannot identify which session to end and throws 'end requires a file path' immediately, before the health check.

Solutions

  1. Pass the plan file path: `ecc-plan-canvas end path/to/plan.md` (same path used with `open`).
  2. Guard shell variables: `: "${PLAN_FILE:?PLAN_FILE not set}"` before invoking end.
  3. If the session is already gone, note cmdEnd returns the server's body regardless — once a valid path is supplied, 'no-server' or an already-ended status is a non-error outcome.
  4. Fix any wrapper script so positional arguments are forwarded.

Example fix

# before
ecc-plan-canvas end

# after
ecc-plan-canvas end "$PLAN_FILE"   # e.g. ecc-plan-canvas end docs/plans/feature.plan.md
Defensive patterns

Strategy: validation

Validate before calling

if (!PLAN_FILE) { echo 'usage: ecc-plan-canvas end <file>' >&2; exit 2; }

Prevention

When it happens

Trigger: Running `ecc-plan-canvas end` with no positional argument — omitted path, empty shell variable, or an npm-script/alias wrapper that does not forward arguments.

Common situations: Cleanup steps in CI where `END_FILE=$PLAN_FILE` was never set; ending a session from a different directory and assuming the tool infers 'the current plan'; typos where the path was given to the wrong flag.

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/338010ee18720f92. Report an issue: GitHub.

Appendix: source

Thrown at scripts/plan-canvas.js:329

// 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',
    sessions: waiting,
    next_step: waiting.length
      ? 'Run `ecc-plan-canvas await <file>` for each file above to receive the messages.'
      : 'No canvas feedback is waiting.'
  };
}

async function cmdEnd(file, { port }) {
  if (!file) throw new Error('end requires a file path');
  if (!(await healthCheck(port))) return { status: 'no-server' };
  const res = await request(port, 'POST', '/api/end', { file: path.resolve(file) });
  return res.body;
}

async function cmdStop({ stateDir, port }) {
  if (!(await healthCheck(port))) return { status: 'not running' };
  await request(port, 'POST', '/shutdown').catch(() => {});
  fs.rmSync(serverInfoPath(stateDir), { force: true });
  return { status: 'stopping' };
}

async function cmdServer(args, { stateDir, port }) {
  const portArg = valueAfter(args, '--port');
  const hostArg = valueAfter(args, '--host');
  const listenPort = portArg !== null ? Number.parseInt(portArg, 10) : port;
  const store = createSessionStore({ stateDir });
  let shuttingDown = false;

View on GitHub (pinned to 8321021c54)