tinyhumansai/openhuman · error
RPC envelope contains undefined data
Error message
RPC envelope contains undefined data
What it means
Thrown by unwrapEnvelope() in threadApi when the RPC response is an object that has a `data` key but its value is undefined. The convention is that list/get handlers return {data: T}; an envelope whose data key exists yet holds undefined means the core built an envelope without content — distinct from a bare payload (returned as-is) and from a null response (also passed through, since 'data' in response is false for null). This guards every thread API call routed through unwrapEnvelope.
Solutions
- Log the full response object at the call site to see whether data is null-but-present (core emitted an empty envelope) vs the payload riding a different key.
- Restart/update the core so threads handlers populate data.
- Check the specific RPC method that failed (the throw is generic) in core logs.
- Fix call sites that re-wrap responses in an extra {data: ...} layer.
Example fix
// before
if (envelope.data === undefined) {
throw new Error('RPC envelope contains undefined data');
}
return envelope.data;
// after — include method context and tolerate null for optional reads
if (envelope.data === undefined) {
throw new Error(`RPC envelope contains undefined data (method: ${method})`);
}
return envelope.data;
// for optional reads, prefer: return envelope.data ?? null; Defensive patterns
Strategy: type-guard
Type guard
function isEnvelopeWithDefinedData<T>(v: unknown): v is { data: T } {
return Boolean(v) && typeof v === 'object' && 'data' in (v as object)
&& (v as { data: unknown }).data !== undefined;
} Try / catch
try {
return unwrapEnvelope<Thread>(response);
} catch (e) {
if (e instanceof Error && e.message === 'RPC envelope contains undefined data') {
// core emitted an empty envelope — restart/update core; do not retry blindly
throw new Error('Thread data unavailable (empty RPC envelope) — restart Core.');
}
throw e;
} Prevention
- Never re-wrap a response in an extra {data: ...} layer at call sites.
- Log the failing RPC method alongside this error for triage.
- Keep handler return shapes ({data: T}) in lockstep with unwrapEnvelope.
When it happens
Trigger: Core handler returns Ok(json!({"data": null})) or serializes a struct with data: Option::None; TypeScript caller re-wraps a result ({data: response.data}) where response.data was already missing; version skew where a handler's data field was renamed.
Common situations: Creating/fetching threads against an older core; a handler edge case (e.g. thread not found) that still emits the envelope skeleton; tests double-wrapping envelopes.
Related errors
- Model test RPC returned no result for
- OPENAI_CODEX_OAUTH_MISSING_AUTH_URL
- provider_surfaces_list_queue: unexpected empty response
- threads_token_usage returned an empty envelope
- Channel connect response missing status
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/dd94586b22045af2.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/services/api/threadApi.ts:40
ListTurnStatesResponse,
PersistedTurnState,
PutTaskBoardResponse,
RunEvent,
RunEventListResponse,
TaskBoard,
TaskBoardCard,
} from '../../types/turnState';
import { callCoreRpc } from '../coreRpcClient';
interface Envelope<T> {
data?: T;
}
function unwrapEnvelope<T>(response: Envelope<T> | T): T {
if (response && typeof response === 'object' && 'data' in response) {
const envelope = response as Envelope<T>;
if (envelope.data === undefined) {
throw new Error('RPC envelope contains undefined data');
}
return envelope.data;
}
return response as T;
}
const generateTitleLog = debug('threadApi.generateTitleIfNeeded');
export const threadApi = {
createNewThread: async (labels?: string[]): Promise<Thread> => {
const response = await callCoreRpc<Envelope<Thread>>({
method: 'openhuman.threads_create_new',
params: { labels },
});
return unwrapEnvelope(response);
},
getThreads: async (): Promise<ThreadsListData> => {View on GitHub (pinned to 7491200858)