tinyhumansai/openhuman · warning

threads_token_usage returned an empty envelope

Error message

threads_token_usage returned an empty envelope

What it means

Thrown by fetchThreadTokenUsage() when the 'openhuman.threads_token_usage' response's .data is falsy (null, undefined, or the whole response missing). The contract says a thread with no completed turns returns zeros with hasUsage: false — so an empty envelope means the core either predates the method's zero-fill behavior, returned null for an unknown thread_id, or wrapped nothing.

Source

Thrown at app/src/services/api/threadUsageApi.ts:70

  subagents?: ThreadSubagentUsageWire[];
}

interface Envelope<T> {
  data?: T;
}

/**
 * Fetch a thread's persisted token/cost totals from the core (read back from
 * its session transcripts). Returns zeros with `hasUsage: false` for a thread
 * that has no completed turns yet.
 */
export async function fetchThreadTokenUsage(threadId: string): Promise<ThreadTokenUsage> {
  const response = await callCoreRpc<Envelope<ThreadTokenUsageWire>>({
    method: 'openhuman.threads_token_usage',
    params: { thread_id: threadId },
  });
  const d = response?.data;
  if (!d) throw new Error('threads_token_usage returned an empty envelope');
  return {
    threadId: d.thread_id,
    inputTokens: d.input_tokens,
    outputTokens: d.output_tokens,
    cachedInputTokens: d.cached_input_tokens,
    costUsd: d.cost_usd,
    turnCount: d.turn_count,
    lastTurnInputTokens: d.last_turn_input_tokens,
    lastTurnOutputTokens: d.last_turn_output_tokens,
    contextWindow: d.context_window,
    model: d.model,
    updated: d.updated,
    hasUsage: d.has_usage,
    subagents: (d.subagents ?? []).map(s => ({
      agentId: s.agent_id,
      inputTokens: s.input_tokens,
      outputTokens: s.output_tokens,
      costUsd: s.cost_usd,

View on GitHub (pinned to a221052e0d)

Solutions

  1. Verify the thread exists on the core side (threads list / the chat itself loads)
  2. Update core and app together — newer cores return a zeroed record with hasUsage: false instead of null
  3. If it persists, curl openhuman.threads_token_usage with the thread_id and inspect the raw result
  4. As a client-side mitigation, catch this specific error and render zeros/hasUsage: false (usage display is non-critical)

Example fix

// before
const usage = await fetchThreadTokenUsage(threadId);

// after
let usage: ThreadTokenUsage;
try {
  usage = await fetchThreadTokenUsage(threadId);
} catch (e) {
  if (e instanceof Error && e.message.includes('empty envelope')) {
    usage = zeroUsage(threadId); // display zeros, mark hasUsage: false
  } else throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Skip the fetch for threads you know are brand-new if the UI can tolerate zeros:
if (!threadHasCompletedTurns(threadId)) renderZeroUsage(); // avoids calling on older cores

Type guard

function isThreadUsageEnvelope(v: unknown): v is { data: { thread_id: string; input_tokens: number } } {
  const r = v as Record<string, unknown> | null | undefined;
  const d = r?.data as Record<string, unknown> | undefined;
  return !!d && typeof d.thread_id === 'string' && typeof d.input_tokens === 'number';
}

Try / catch

try {
  const usage = await fetchThreadTokenUsage(threadId);
  renderUsage(usage);
} catch (e) {
  if (e instanceof Error && e.message.includes('empty envelope')) {
    renderUsage(zeroUsage(threadId)); // usage display degrades to zeros
  } else throw e;
}

Prevention

When it happens

Trigger: Calling fetchThreadTokenUsage(threadId) for a thread id the core does not know (deleted, wrong workspace, or not yet persisted); an older core returning {data: null} instead of a zeroed usage record; a mock resolving {data: null}.

Common situations: Opening thread usage/cost UI immediately after creating a thread on an older core; pointing the app at a workspace whose threads db was reset; frontend/core version skew on the threads_token_usage controller.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/898fa1c0c29bafea. Report an issue: GitHub.