tinyhumansai/openhuman · error
Channel connect response missing status
Error message
Channel connect response missing status
What it means
Thrown by normalizeConnectResult() when a channel connect RPC response's `status` is missing or not a string (empty string after the typeof fallback fails the !status check). Status is the primary discriminator of a connect outcome (e.g. connected/pending_auth/restart_required); every other field (restart_required, auth_action, message) is optional and defaulted. Only a missing/non-string status is fatal.
Solutions
- Restart/update the core so the channel connect controller returns {status: string, ...}.
- Log the raw record on failure to check whether the field is renamed, nested, or the record is an error object that should have been thrown upstream.
- Fix mocks/tests to include a status string.
- If the core returns a nested payload (result.data), adjust expectObject/unwrap to target the right layer before validation.
Example fix
// before
const status = typeof record.status === 'string' ? record.status : '';
if (!status) {
throw new Error('Channel connect response missing status');
}
// after — surface what WAS returned to speed up triage
const status = typeof record.status === 'string' ? record.status
: typeof record.state === 'string' ? record.state : '';
if (!status) {
throw new Error(`Channel connect response missing status (got keys: ${Object.keys(record).join(', ')})`);
} Defensive patterns
Strategy: type-guard
Type guard
function isChannelConnectResult(v: unknown): v is { status: string; restart_required?: boolean; auth_action?: string; message?: string } {
return typeof (v as { status?: unknown } | null)?.status === 'string'
&& ((v as { status: string }).status.length > 0);
} Try / catch
try {
const r = await connectChannel(provider);
} catch (e) {
if (e instanceof Error && e.message === 'Channel connect response missing status') {
notify('Could not read the connect result — restart Core and retry.');
} else throw e;
} Prevention
- Include Object.keys(record) in validator failures for fast field-name triage.
- Keep the core's connect controller and this validator in the same PR when changing the response shape.
- Handle restart_required/auth_action after validating status, not before.
When it happens
Trigger: Core connect handler returned an error record or empty object through a success envelope; field renamed (state vs status) across versions; unwrapCliEnvelope stripped the payload layer containing status; provider threw mid-connect and a stub {ok:false} was returned.
Common situations: Connecting a messaging channel (Telegram/Slack/Discord/WhatsApp) on a version-skewed core; a provider-specific failure path that forgot to set status; test mocks omitting status.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Discord link complete response missing required boolean…
- Discord link start response missing required string field…
- Discord link start response missing required string field…
- allowlist persist unsupported for channel
- cannot persist empty identity
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/56344229f3a02618.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/services/api/channelConnectionsApi.ts:103
}
function expectDiscordLinkComplete(payload: unknown): DiscordLinkCheckResult {
const record = expectObject<Record<string, unknown>>(payload, 'Discord link complete');
if (typeof record.linked !== 'boolean') {
throw new Error('Discord link complete response missing required boolean field: linked');
}
const details =
record.details !== undefined && record.details !== null
? (record.details as Record<string, unknown>)
: null;
return { linked: record.linked, details };
}
function normalizeConnectResult(payload: unknown): ChannelConnectionResult {
const record = expectObject<Record<string, unknown>>(payload, 'Channel connect');
const status = typeof record.status === 'string' ? record.status : '';
if (!status) {
throw new Error('Channel connect response missing status');
}
return {
status,
restart_required: Boolean(record.restart_required),
auth_action: typeof record.auth_action === 'string' ? record.auth_action : undefined,
message: typeof record.message === 'string' ? record.message : undefined,
};
}
function normalizePermissionCheck(payload: unknown): BotPermissionCheck {
const record = expectObject<Record<string, unknown>>(payload, 'Discord permission check');
const missing = Array.isArray(record.missing_permissions)
? record.missing_permissions.filter((perm): perm is string => typeof perm === 'string')
: [];
return {
can_view_channel: Boolean(record.can_view_channel),
can_send_messages: Boolean(record.can_send_messages),
can_read_message_history: Boolean(record.can_read_message_history),View on GitHub (pinned to 7491200858)