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
- Run `node scripts/plan-canvas.js open <file>` to (re)create the session, then retry typing.
- Check server health: the cmdTyping path calls healthCheck first — if that passed but typing still 4xx'd, the session key is stale; re-open.
- Confirm the port in the state dir matches the running server (serverInfoPath) and no other service hijacked it.
- 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
- Re-open the session if it may have idled out before sending presence.
- Confirm the running server is the plan-canvas server (check serverInfoPath), not a port-hijacker.
- Never let a typing failure fail the agent turn — wrap it.
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
- HTTP ${res.status}
- open failed (HTTP ${res.statusCode})
- typing requires a file path
- Unknown control-pane action: ${actionId}
- Expected JSON response from control pane:
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/819dee74b0b2ddde.
Report an issue: GitHub.