CherryHQ/cherry-studio · error · Error

typeof failure === 'string' ? failure : JSON.stringify(failu

Error message

typeof failure === 'string' ? failure : JSON.stringify(failure ?? 'unknown error')

What it means

Thrown by runFormulaFiber() when a Kimi (Moonshot) formula fiber call does not yield usable output: status !== 'succeeded', or succeeded but with empty output/encrypted_output. The message is the fiber's own error payload (stringified if structured). It surfaces as a tool-execution failure so the SDK marks the tool part failed rather than returning an empty 'search returned nothing' result.

Source

Thrown at src/main/ai/provider/custom/moonshotProvider.ts:60

  args: unknown
): Promise<string> {
  const doFetch = settings.fetch ?? globalThis.fetch
  const response = await doFetch(`${withoutTrailingSlash(settings.baseURL)}/formulas/${formulaUri}/fibers`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${settings.apiKey}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ name, arguments: JSON.stringify(args ?? {}) })
  })

  const fiber = (await response.json().catch(() => ({}))) as FormulaFiber
  if (fiber.status === 'succeeded') {
    const output = fiber.context?.output || fiber.context?.encrypted_output
    if (output) return output
  }
  // Throw rather than returning an "Error: …" string as a normal result: the SDK still hands the
  // message to the model, and the tool part is marked failed instead of masquerading as a search
  // that returned nothing.
  const failure = fiber.error ?? fiber.context?.error ?? fiber.context?.output
  throw new Error(typeof failure === 'string' ? failure : JSON.stringify(failure ?? 'unknown error'))
}

/**
 * The declaration mirrors `GET /formulas/moonshot/web-search:latest/tools`. It is inlined because the
 * AI SDK needs the schema synchronously when the tool set is built; the fiber call is what actually
 * runs the search, so a drifted description costs nothing.
 */
export interface KimiFormulaCredentials {
  apiKey?: string
  baseURL?: string
  fetch?: FetchFunction
}

/** Build the tool from this request's serving credential (see the factory in extensions.ts). */
export function createKimiWebSearchToolFor(credentials: KimiFormulaCredentials) {
  return createKimiWebSearchTool((args) =>
    runFormulaFiber(
      {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Inspect the thrown message: a 401-flavored error means the apiKey is wrong/missing; a rate-limit message means back off; an 'unauthorized formula' message means enable web-search in the Kimi account.
  2. Confirm credentials.baseURL resolves to https://api.moonshot.cn/v1 (or the matching region) and that /formulas/.../fibers is reachable.
  3. Verify the tool input shape matches { query: string }; re-encoding via JSON.stringify(args) must produce the contract the fiber expects.
  4. If the fiber is protected and returns no encrypted_output, fall back to a non-formula search path or instruct the user to grant the scope.

Example fix

// before
const out = await runFormulaFiber(creds, uri, name, args)
// after — guard the fiber shape and rethrow a typed error
const resp = await doFetch(...)
const fiber = await resp.json().catch(() => ({}))
if (fiber.status !== 'succeeded' || !(fiber.context?.output || fiber.context?.encrypted_output)) {
  throw new Error(`Kimi fiber '${uri}' failed: ${typeof fiber.error === 'string' ? fiber.error : JSON.stringify(fiber.error ?? fiber)}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate credentials and input shape before the fiber call
if (!settings.apiKey) throw new Error('Moonshot apiKey is required')
if (typeof args !== 'object' || args === null || typeof (args as any).query !== 'string') {
  throw new Error('Kimi web-search fiber requires { query: string }')
}

Type guard

export function isFiberFailure(e: unknown): boolean {
  return e instanceof Error && /fiber|Kimi|moonshot/i.test(e.message)
}

Try / catch

try {
  await runFormulaFiber(creds, uri, name, args)
} catch (e) {
  // Surface as a tool-error result so the model sees the reason
  return { toolResult: { error: e instanceof Error ? e.message : String(e) } }
}

Prevention

When it happens

Trigger: POST /formulas/{uri}/fibers returns { status: 'failed' } or { error: ... } or a succeeded fiber with empty output/encrypted_output. Concrete causes: invalid/expired Moonshot apiKey, web-search formula disabled for the account, rate limit on the formula channel, malformed arguments JSON, or the protected formula's encrypted_output requires a session the key doesn't have.

Common situations: First use of Kimi web-search tool with an unprivileged key, baseURL pointing at a non-formula endpoint, arguments schema drift (e.g. { query } renamed), or account not whitelisted for protected formulas.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/de8fb0d6dac1cf12. Report an issue: GitHub.