affaan-m/ECC · error · Error

typing failed (HTTP )

Error message

typing failed (HTTP ${res.statusCode})

What it means

cmdTyping POSTs the presence state to /api/session/<key>/typing and expects HTTP 200. On any other status it throws 'typing failed (HTTP <status>)', unless the response body carried an explicit error message, which is preferred. Because the body fallback exists, a bare status message usually means the server returned an unexpected body shape or an empty error.

Solutions

  1. Check the returned status: 404 means the session is gone — rerun `ecc-plan-canvas open <file>` to re-register, then resume typing signals.
  2. Verify the --state value is one the server accepts (e.g. typing/thinking/idle); fix typos.
  3. Confirm the CLI and server come from the same ECC version (`ecc-plan-canvas stop` then reopen) so the route matches.
  4. Wrap the typing call defensively in automation — per design a failed typing signal must never derail the actual work, so catch and log rather than abort.

Example fix

// before (aborts the pipeline)
await cmdTyping(file, args, ctx);

// after (typing is best-effort)
try {
  await cmdTyping(file, args, ctx);
} catch (err) {
  console.warn(`typing signal skipped: ${err.message}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

const VALID_STATES = new Set(['typing', 'thinking', 'idle']);
if (!VALID_STATES.has(state)) throw new Error(`unsupported typing state: ${state}`);
if (!(await healthCheck(port))) return; // nothing to signal

Try / catch

try {
  await cmdTyping(file, args, ctx);
} catch (err) {
  console.warn(`typing signal skipped: ${err.message}`); // 404 => session gone; reopen if needed
}

Prevention

When it happens

Trigger: Posting a typing signal when the server rejects the request: 404 because the session key no longer exists (server restarted or session ended via /api/end), 400 for an unsupported --state value, 500 on a server-side state write failure.

Common situations: The canvas server restarted (idle timeout) and wiped sessions while an agent keeps signaling typing; a typo'd --state like 'writting' rejected with 400; two ECC versions disagreeing on the /typing route so the old client hits a moved endpoint (404/405).

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: 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 8321021c54)