{"record":{"id":"5c306a3243765505","repo":"CherryHQ/cherry-studio","slug":"ppio-api-request-timeout-after-timeout-1000-s","errorCode":null,"errorMessage":"PPIO API request timeout after ${timeout / 1000}s","messagePattern":"PPIO API request timeout after (.+?)s","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/provider/custom/ppio/ppioTransport.ts","lineNumber":170,"sourceCode":"    }\n\n    try {\n      const response = await fetch(url, fetchOptions)\n\n      if (!response.ok) {\n        const errorText = (await response.text().catch(() => '')).slice(0, 500)\n        throw new PpioApiError(`PPIO API error: ${response.status} - ${errorText}`, response.status)\n      }\n\n      const data = await response.json()\n      return data as T\n    } catch (error) {\n      if (error instanceof Error && error.name === 'AbortError') {\n        if (externallyAborted) {\n          throw createAbortError('PPIO API request aborted')\n        }\n\n        throw new Error(`PPIO API request timeout after ${timeout / 1000}s`)\n      }\n      throw error\n    } finally {\n      clearTimeout(timeoutId)\n      externalSignal?.removeEventListener('abort', onExternalAbort)\n    }\n  }\n\n  async submit(input: ImageGenerationSubmitInput): Promise<{ taskId?: string; imageUrls?: string[] }> {\n    const bagParams = input.providerParams as PpioProviderParams\n    const descriptor = input.modelDescriptor\n    if (!descriptor) {\n      throw new Error(`Unknown model: ${input.modelId}`)\n    }\n\n    // Native AI SDK fields (size / seed) land on `input.*` post-canonicalGenerate\n    // partition, not in the providerOptions bag. Merge them into a unified\n    // view so the per-model builders below can read uniformly. `ppioSeed`","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/provider/custom/ppio/ppioTransport.ts#L152-L188","documentation":"Thrown when PpioTransport.request()'s internal timeout AbortController fires before the fetch resolves, and the abort did NOT come from the caller's external signal (the external-abort path throws a synthesized AbortError instead). Reports the configured timeout in seconds.","triggerScenarios":"request() with a `timeout` (DEFAULT_TIMEOUT default, 120000ms for submit, 10000ms for poll getTaskResult) where fetch neither resolves nor is externally aborted in time. Happens during vendor slowness, cold model load, network stalls, or proxy buffering.","commonSituations":"Submit exceeding the 120s budget on a slow link, poll GET exceeding 10s during vendor overload, proxy dropping long-lived connections, or offline/metered network.","solutions":["Retry once with backoff — most PPIO timeouts are transient.","Raise options.timeout for submit (120000ms) if the vendor is known slow; raise poll timeout (10000ms) if getTaskResult consistently stalls.","Verify network/proxy reachability to api.ppio.com; check for corporate proxies that drop long POSTs.","Confirm the user is not on a degraded connection."],"exampleFix":"// before\nconst r = await this.request(endpoint, body, 'POST', { timeout: 120000 })\n// after — escalate for slow vendors\nconst r = await this.request(endpoint, body, 'POST', { timeout: slowVendor ? 300000 : 120000 })","handlingStrategy":"retry","validationCode":"// No request-time validation prevents a timeout; budget it\nconst timeout = slowVendor ? 300000 : DEFAULT_TIMEOUT","typeGuard":"export function isPpioTimeoutError(e: unknown): boolean {\n  return e instanceof Error && /^PPIO API request timeout after/.test(e.message)\n}","tryCatchPattern":"try {\n  await transport.submit(input)\n} catch (e) {\n  if (isPpioTimeoutError(e)) await backoffRetry(() => transport.submit(input), { maxAttempts: 2 })\n  throw e\n}","preventionTips":["Size the per-call timeout to the operation (submit needs more than poll).","Retry once on timeout before surfacing.","Provide a cancel affordance for long submits."],"tags":["timeout","network","ppio","abort","image-generation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}