affaan-m/ECC · error · Error

open failed (HTTP ${res.statusCode})

Error message

open failed (HTTP ${res.statusCode})

What it means

Thrown by cmdOpen in scripts/plan-canvas.js when POST /api/sessions returns a status code that is neither 200 (success) nor 409 (conflict, session already open). The message prefers the server's own error body if present, falling back to the generic HTTP-status form shown. This indicates the canvas server received the request but rejected it.

Source

Thrown at scripts/plan-canvas.js:233

async function cmdStatus({ stateDir, port }) {
  const health = await healthCheck(port);
  if (!health) {
    return { server: 'not running', hint: 'open an artifact to start one', stateDir };
  }
  const sessions = await request(port, 'GET', '/api/sessions');
  return { server: `http://${DEFAULT_HOST}:${port}`, version: health.version, sessions: sessions.body.sessions };
}

async function cmdOpen(file, args, { stateDir, port }) {
  if (!file) throw new Error('open requires a file path');
  if (!fs.existsSync(path.resolve(file))) throw new Error(`artifact not found: ${file}`);
  await ensureServer({ stateDir, port });
  const res = await request(port, 'POST', '/api/sessions', {
    file: path.resolve(file),
    reopen: args.includes('--reopen')
  });
  if (res.statusCode === 409) return res.body;
  if (res.statusCode !== 200) throw new Error(res.body.error || `open failed (HTTP ${res.statusCode})`);
  const url = `http://${DEFAULT_HOST}:${port}${res.body.url}`;
  const launched = args.includes('--no-open') ? false : openBrowser(url);
  return {
    status: 'open',
    url,
    browser: launched ? 'opened' : 'not opened',
    next_step:
      'Run `ecc-plan-canvas await <file>` and leave it running; it returns when the human sends feedback, a verdict, or ends the session.'
  };
}

function awaitRequest(port, key, timeoutMs) {
  if (!/^[a-f0-9]{12}$/.test(key)) throw new Error('invalid plan-canvas session key');
  const params = new URLSearchParams({ key });
  if (timeoutMs !== null) params.set('timeoutMs', String(timeoutMs));
  return new Promise((resolve, reject) => {
    const req = http.request(
      requestOptions(port, 'GET', `/api/await?${params}`, {}),

View on GitHub (pinned to 01e15490f0)

Solutions

  1. If res.body.error was surfaced, address that specific message first.
  2. Restart the server (`node scripts/plan-canvas.js stop`, then retry open) to clear transient state.
  3. Inspect server.log in the state directory for the stack trace behind a 500.
  4. Ensure the ECC version of the running server matches the CLI (a version mismatch normally triggers a clean restart, but a stale server.json can defeat that).
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await cmdOpen(file, args, context);
} catch (err) {
  if (/open failed \(HTTP/.test(err.message)) {
    const log = fs.readFileSync(path.join(stateDir, 'server.log'), 'utf8');
    console.error('Session creation failed. server.log tail:\n', log.split('\n').slice(-30).join('\n'));
    await cmdStop(context); // reset before retry
  }
  throw err;
}

Prevention

When it happens

Trigger: The server returns 400 (malformed session body), 500 (internal error during session creation), or any other non-200/409 code. If res.body.error is set by the server, that message is used; otherwise the generic `open failed (HTTP <code>)` is thrown.

Common situations: The artifact file watcher cannot read the file at session-creation time; the session store on disk is corrupted; a server bug during canonicalization; a version-skewed server that does not understand the request shape.

Related errors


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