affaan-m/ECC · warning · Error

typing failed (HTTP ${res.statusCode})

Error message

typing failed (HTTP ${res.statusCode})

What it means

Thrown by cmdTyping when the canvas server responds to the typing POST with a non-200 status and the response body carried no `error` field. It is a fallback message that surfaces the raw HTTP status so the caller can tell a transport/session problem apart from a missing file. The preferred server-supplied `res.body.error` is used when present.

Source

Thrown at scripts/plan-canvas.js:307

      : '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',
    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.'
  };

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Run `node scripts/plan-canvas.js open <file>` to (re)create the session, then retry typing.
  2. Check server health: the cmdTyping path calls healthCheck first — if that passed but typing still 4xx'd, the session key is stale; re-open.
  3. Confirm the port in the state dir matches the running server (serverInfoPath) and no other service hijacked it.
  4. Because typing is fire-and-forget, log the status but do not fail the parent workflow.

Example fix

// before
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})`);
// after — tolerate stale-session so presence never breaks the agent
const res = await request(port, 'POST', `/api/session/${key}/typing`, { state }).catch(() => null);
if (!res || res.statusCode !== 200) {
  console.error(`[plan-canvas] typing signal skipped (HTTP ${res ? res.statusCode : 'no-response'})`);
  return { status: 'skipped' };
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Health + session existence check before typing
async function sessionExists(port, key) {
  const res = await request(port, 'GET', '/api/sessions');
  return Array.isArray(res.body) && res.body.some(s => s.key === key);
}

Type guard

function isTypingOk(res) {
  return res && typeof res === 'object' && res.statusCode === 200;
}

Try / catch

let presence = null;
try {
  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})`);
  presence = res.body.presence;
} catch (err) {
  console.error(`[plan-canvas] typing skipped: ${err.message}`);
  // non-fatal: the real work continues
}

Prevention

When it happens

Trigger: The session key has no active session server-side (404); the server restarted and lost in-memory state; the typing endpoint rejected the `state` value; network/proxy returned a 5xx; port collision reaching a different service.

Common situations: Canvas server idled out and was reaped but the caller kept polling; `open` never ran for that file so no session row exists; server version mismatch where the typing route was renamed; another process grabbed the same port.

Related errors


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