affaan-m/ECC · error · Error

end requires a file path

Error message

end requires a file path

What it means

Thrown by cmdEnd when the `end` subcommand is called without a positional file path. The end command POSTs to `/api/end` with `path.resolve(file)`, so a file is mandatory to identify which review session to terminate. It is the agent-side way to close a session after delivering final updates.

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

Solutions

  1. Pass the file: `node scripts/plan-canvas.js end .claude/plans/feature.plan.md`.
  2. If you need to stop the whole server regardless of session, use `node scripts/plan-canvas.js stop` instead.
  3. Track open files in your orchestrator so the end call always has a concrete path.

Example fix

// before
node scripts/plan-canvas.js end
// after
node scripts/plan-canvas.js end .claude/plans/feature.plan.md
Defensive patterns

Strategy: validation

Validate before calling

function validateEndFile(file) {
  if (typeof file !== 'string' || file.trim() === '') {
    throw new Error('cmdEnd: file argument is required');
  }
  return file;
}

Type guard

function isEndFile(arg) {
  return typeof arg === 'string' && arg.length > 0 && !arg.startsWith('--');
}

Try / catch

try {
  await cmdEnd(file, ctx);
} catch (err) {
  if (err.message === 'end requires a file path') {
    console.error('Usage: node scripts/plan-canvas.js end <file>');
    process.exit(2);
  }
  throw err;
}

Prevention

When it happens

Trigger: Running `node scripts/plan-canvas.js end` with no file; passing a flag in the file slot; a cleanup hook that calls end generically without knowing which plan was open.

Common situations: Agent shutdown routine calls `end` for all sessions but one file variable was empty; user misreads usage and thinks `end` with no arg ends all; orphaned session from a crashed `open` that the caller tries to end by name that no longer exists.

Related errors


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