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

  1. Restart/update the core so the channel connect controller returns {status: string, ...}.
  2. 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.
  3. Fix mocks/tests to include a status string.
  4. 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

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


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)