tinyhumansai/openhuman · error
Discord link start response missing required string field…
Error message
Discord link start response missing required string field: instructions
What it means
Thrown by expectDiscordLinkStart() when the link-start response object lacks a string `instructions` field — the human-readable steps shown to the user (typically 'DM this code to the bot'). linkToken may already be present; the validator requires both fields, so this fires specifically when the core returned a token but no instructions text. It is the second half of the response-shape contract for Discord pairing.
Solutions
- Update/restart the core so link.start returns both linkToken and instructions.
- Log the raw record at the validator to confirm whether instructions is absent, null, or renamed (e.g. instruction / steps).
- If the field is merely optional in your core version, default it in the validator with a generic instruction string instead of throwing.
- Check the core's Discord connections controller code for the response struct to align field names.
Example fix
// before
if (typeof record.instructions !== 'string') {
throw new Error('Discord link start response missing required string field: instructions');
}
return { linkToken: record.linkToken, instructions: record.instructions };
// after — graceful default keeps pairing usable
const instructions = typeof record.instructions === 'string' && record.instructions
? record.instructions
: `Send this code to the bot in a DM: ${record.linkToken}`;
return { linkToken: record.linkToken, instructions }; Defensive patterns
Strategy: type-guard
Type guard
function hasInstructions(v: unknown): v is { instructions: string } {
return typeof (v as { instructions?: unknown } | null)?.instructions === 'string';
} Try / catch
try {
const { instructions } = await startDiscordLink();
} catch (e) {
if (e instanceof Error && e.message.includes('missing required string field: instructions')) {
// core is older — fall back to a generic instruction around the token
showGenericInstructions();
} else throw e;
} Prevention
- Default the instructions text from the token when the field is optional in your core version.
- Align mock responses with the core's actual response struct.
- Restart core after app updates to avoid schema drift.
When it happens
Trigger: Core handler version returns only {linkToken} (older/other implementation); instructions exists but is not a string (null, object); a serialization path drops the field when it contains formatting; envelope unwrap mis-targeting nested result objects.
Common situations: Cross-version pairing: UI from a newer release running against an older core binary; a fork/other host implementing the same RPC with a reduced field set; null returned by a core when the provider has no localized instructions.
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…
- Channel connect response missing status
- Core returned an empty backend URL
- Core RPC returned an error
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/426b89fb2d33806b.
Report an issue: GitHub.
Appendix: 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 7491200858)