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
- Check the returned status: 404 means the session is gone — rerun `ecc-plan-canvas open <file>` to re-register, then resume typing signals.
- Verify the --state value is one the server accepts (e.g. typing/thinking/idle); fix typos.
- Confirm the CLI and server come from the same ECC version (`ecc-plan-canvas stop` then reopen) so the route matches.
- 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
- Wrap typing calls in try/catch — by design they must never derail real work.
- On 404, reopen the session rather than retrying the same dead key.
- Keep CLI and server on the same ECC version so the /typing route matches.
- Health-check the server before emitting presence signals.
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
- open failed (HTTP )
- await requires a file path
- end requires a file path
- invalid plan-canvas session key
- typing requires a file path
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)