{"record":{"id":"de8fb0d6dac1cf12","repo":"CherryHQ/cherry-studio","slug":"typeof-failure-string-failure-json-strin","errorCode":null,"errorMessage":"typeof failure === 'string' ? failure : JSON.stringify(failure ?? 'unknown error')","messagePattern":"typeof failure === 'string' \\? failure : JSON\\.stringify\\(failure \\?\\? 'unknown error'\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/provider/custom/moonshotProvider.ts","lineNumber":60,"sourceCode":"  args: unknown\n): Promise<string> {\n  const doFetch = settings.fetch ?? globalThis.fetch\n  const response = await doFetch(`${withoutTrailingSlash(settings.baseURL)}/formulas/${formulaUri}/fibers`, {\n    method: 'POST',\n    headers: { Authorization: `Bearer ${settings.apiKey}`, 'Content-Type': 'application/json' },\n    body: JSON.stringify({ name, arguments: JSON.stringify(args ?? {}) })\n  })\n\n  const fiber = (await response.json().catch(() => ({}))) as FormulaFiber\n  if (fiber.status === 'succeeded') {\n    const output = fiber.context?.output || fiber.context?.encrypted_output\n    if (output) return output\n  }\n  // Throw rather than returning an \"Error: …\" string as a normal result: the SDK still hands the\n  // message to the model, and the tool part is marked failed instead of masquerading as a search\n  // that returned nothing.\n  const failure = fiber.error ?? fiber.context?.error ?? fiber.context?.output\n  throw new Error(typeof failure === 'string' ? failure : JSON.stringify(failure ?? 'unknown error'))\n}\n\n/**\n * The declaration mirrors `GET /formulas/moonshot/web-search:latest/tools`. It is inlined because the\n * AI SDK needs the schema synchronously when the tool set is built; the fiber call is what actually\n * runs the search, so a drifted description costs nothing.\n */\nexport interface KimiFormulaCredentials {\n  apiKey?: string\n  baseURL?: string\n  fetch?: FetchFunction\n}\n\n/** Build the tool from this request's serving credential (see the factory in extensions.ts). */\nexport function createKimiWebSearchToolFor(credentials: KimiFormulaCredentials) {\n  return createKimiWebSearchTool((args) =>\n    runFormulaFiber(\n      {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/provider/custom/moonshotProvider.ts#L42-L78","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Confirm credentials.baseURL resolves to https://api.moonshot.cn/v1 (or the matching region) and that /formulas/.../fibers is reachable.","Verify the tool input shape matches { query: string }; re-encoding via JSON.stringify(args) must produce the contract the fiber expects.","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."],"exampleFix":"// before\nconst out = await runFormulaFiber(creds, uri, name, args)\n// after — guard the fiber shape and rethrow a typed error\nconst resp = await doFetch(...)\nconst fiber = await resp.json().catch(() => ({}))\nif (fiber.status !== 'succeeded' || !(fiber.context?.output || fiber.context?.encrypted_output)) {\n  throw new Error(`Kimi fiber '${uri}' failed: ${typeof fiber.error === 'string' ? fiber.error : JSON.stringify(fiber.error ?? fiber)}`)\n}","handlingStrategy":"try-catch","validationCode":"// Validate credentials and input shape before the fiber call\nif (!settings.apiKey) throw new Error('Moonshot apiKey is required')\nif (typeof args !== 'object' || args === null || typeof (args as any).query !== 'string') {\n  throw new Error('Kimi web-search fiber requires { query: string }')\n}","typeGuard":"export function isFiberFailure(e: unknown): boolean {\n  return e instanceof Error && /fiber|Kimi|moonshot/i.test(e.message)\n}","tryCatchPattern":"try {\n  await runFormulaFiber(creds, uri, name, args)\n} catch (e) {\n  // Surface as a tool-error result so the model sees the reason\n  return { toolResult: { error: e instanceof Error ? e.message : String(e) } }\n}","preventionTips":["Confirm web-search formula is enabled for the Moonshot account before relying on it.","Keep the tool input shape ({ query: string }) in sync with the formula contract.","Return fiber failures as tool-error results rather than swallowing them — the model adapts."],"tags":["moonshot","kimi","formula","tool","api"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}