tinyhumansai/openhuman · error
Discord link start response missing required string field: i
Error message
Discord link start response missing required string field: instructions
What it means
Thrown by expectDiscordLinkStart() when the 'openhuman.channels_discord_link_start' response object's 'instructions' field is missing or not a string (linkToken already passed its check). The managed Discord link flow needs the instructions text to show the user what to paste into Discord, so the frontend refuses a payload without it.
Source
Thrown at app/src/services/api/channelConnectionsApi.ts:82
return unwrapped as T[];
}
function expectObject<T extends object>(payload: unknown, context: string): T {
const unwrapped = unwrapCliEnvelope<unknown>(payload);
const record = asRecord(unwrapped);
if (!record) {
throw new Error(`${context} returned an invalid response shape`);
}
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');View on GitHub (pinned to a221052e0d)
Solutions
- Restart the core / relaunch the app so core and frontend versions match (same fix as the linkToken variant of this error)
- Curl openhuman.channels_discord_link_start on /rpc and verify result.instructions is a string
- Check core logs: the instructions string is usually composed from the bot config; a missing bot handle/config yields no instructions
- If developing the handler, always set instructions (fall back to a static template string core-side rather than omitting the field)
Example fix
// Rust core: never omit the field
// before
if let Some(instructions) = build_instructions() { resp.insert("instructions", instructions); }
// after
resp.insert("instructions", build_instructions().unwrap_or_else(||
"Send !start <token> to the OpenHuman bot in Discord.".to_string())); Defensive patterns
Strategy: type-guard
Type guard
function hasInstructions(v: unknown): v is { instructions: string } {
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.instructions === 'string';
} Try / catch
try {
const r = await channelConnectionsApi.discordLinkStart();
renderInstructions(r.instructions);
} catch (e) {
if (e instanceof Error && e.message.includes('instructions')) {
showFallbackInstructions(r_linkTokenIfAvailable); // static '!start <token>' copy
} else throw e;
} Prevention
- Core-side: default instructions to a static template instead of omitting the field
- Cover the instructions field in the same contract tests as linkToken
- Treat any single-field miss in this payload as version skew and restart/resync builds
When it happens
Trigger: discordLinkStart() resolves but the core returns {linkToken: '...'} with no instructions key, instructions: null / number / object, or a CLI envelope whose result carries only the token. Note: an empty string '' passes — only absence or a non-string type throws.
Common situations: Core version that mints the token but skips building instructions (e.g. bot-invite template missing or feature-flagged off); frontend/core skew after a partial update; a refactor of the Rust response struct dropping or renaming the field.
Related errors
- Discord link start response missing required string field: l
- Discord link complete response missing required boolean fiel
- 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/426b89fb2d33806b.
Report an issue: GitHub.