tinyhumansai/openhuman · error
Discord link complete response missing required boolean fiel
Error message
Discord link complete response missing required boolean field: linked
What it means
Thrown by expectDiscordLinkComplete() while validating the 'openhuman.channels_discord_link_check' response: the payload object must contain 'linked' as a boolean (details is optional and tolerated as null/undefined). The polling loop in the Discord connect UI hits this when the core's link-check answer cannot be interpreted as a link state.
Source
Thrown at app/src/services/api/channelConnectionsApi.ts:90
}
return record as T;
}
function expectDiscordLinkStart(payload: unknown): DiscordLinkStartResult {
const record = expectObject<Record<string, unknown>>(payload, 'Discord link start');
if (typeof record.linkToken !== 'string' || !record.linkToken) {
throw new Error('Discord link start response missing required string field: linkToken');
}
if (typeof record.instructions !== 'string') {
throw new Error('Discord link start response missing required string field: instructions');
}
return { linkToken: record.linkToken, instructions: record.instructions };
}
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,View on GitHub (pinned to a221052e0d)
Solutions
- Restart the core / relaunch the app to resync frontend and core builds
- Curl openhuman.channels_discord_link_check with the pending linkToken and inspect the result object's shape
- Check core logs for link-token lookup failures (expired/garbage token) that may be surfaced as a degenerate payload
- If developing the handler, always include linked: bool in the response, even on lookup failure (linked: false + details.error)
Example fix
// before (core returns an error object through a success path)
{"error": "token not found"}
// after
{"linked": false, "details": {"error": "token not found"}} Defensive patterns
Strategy: type-guard
Validate before calling
// Skip polling when the token is clearly invalid, so the check never runs against a dead token: if (!linkToken.trim()) stopPolling();
Type guard
function isDiscordLinkCheck(v: unknown): v is { linked: boolean; details?: Record<string, unknown> | null } {
const r = v as Record<string, unknown> | null | undefined;
const inner =
r && 'result' in r && 'logs' in r ? (r.result as Record<string, unknown>) : r;
return !!inner && typeof inner.linked === 'boolean';
} Try / catch
try {
const { linked } = await channelConnectionsApi.discordLinkCheck(token);
if (linked) finishLink();
} catch (e) {
if (e instanceof Error && e.message.includes('linked')) {
stopPollingAndWarn('Could not read link status — restart the app and relink.');
} else throw e;
} Prevention
- Poll with a bounded number of attempts so a permanently bad shape cannot loop forever
- Expire link tokens client-side (stop polling after N minutes) to avoid checking dead tokens
- Pin the linked:boolean field in contract tests for channels_discord_link_check
When it happens
Trigger: discordLinkCheck(linkToken) resolves with a payload like {status: 'pending'}, {is_linked: true} (renamed field), {} (unknown/expired token handled by returning an empty object), or a CLI envelope whose result lacks a boolean 'linked'.
Common situations: Core/frontend version skew after a partial update; a Rust refactor renaming 'linked' or nesting it one level deeper; the link token expired and an older core returned an error-shaped object through a success path.
Related errors
- Discord link start response missing required string field: l
- Discord link start response missing required string field: i
- Channel connect response missing status
- ${context} returned an invalid response shape
- provider_surfaces_list_queue: unexpected empty response
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/2b56f280134fb96d.
Report an issue: GitHub.