{"record":{"id":"b49ad8394e07d76a","repo":"toeverything/AFFiNE","slug":"network-error-b49ad8","errorCode":"NETWORK_ERROR","errorMessage":"Network error: ${err.message}","messagePattern":"Network error: (.+?)","errorType":"exception","errorClass":"UserFriendlyError","httpStatus":504,"severity":"error","filePath":"packages/common/nbstore/src/impls/cloud/http.ts","lineNumber":40,"sourceCode":"    const timeoutId =\n      timeout > 0\n        ? setTimeout(() => {\n            abortController.abort(new Error('request timeout'));\n          }, timeout)\n        : undefined;\n\n    const res = await globalThis\n      .fetch(new URL(input, this.serverBaseUrl), {\n        ...init,\n        signal: abortController.signal,\n        headers: {\n          ...this.requestHeaders,\n          ...init?.headers,\n          'x-affine-version': BUILD_CONFIG.appVersion,\n        },\n      })\n      .catch(err => {\n        throw new UserFriendlyError({\n          status: 504,\n          code: 'NETWORK_ERROR',\n          type: 'NETWORK_ERROR',\n          name: 'NETWORK_ERROR',\n          message: `Network error: ${err.message}`,\n          stacktrace: err.stack,\n        });\n      });\n    if (timeoutId) {\n      clearTimeout(timeoutId);\n    }\n    if (!res.ok && res.status !== 404) {\n      if (res.status === 413) {\n        throw new UserFriendlyError({\n          status: 413,\n          code: 'CONTENT_TOO_LARGE',\n          type: 'CONTENT_TOO_LARGE',\n          name: 'CONTENT_TOO_LARGE',","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/common/nbstore/src/impls/cloud/http.ts#L22-L58","documentation":"HttpConnection.fetch wraps globalThis.fetch; any fetch rejection (DNS failure, connection refused, CORS, TLS error, or the built-in 15s default timeout aborting with 'request timeout') is converted into a UserFriendlyError with status 504 and code/type/name NETWORK_ERROR. The original message (e.g. 'Failed to fetch' or 'request timeout') and stack are preserved inside.","triggerScenarios":"Calling connection.fetch/gql while offline; wrong/unreachable serverBaseUrl; expired/self-signed certificates; a request exceeding init.timeout (default 15000ms — the abort surfaces as 'Network error: request timeout'); CORS blocking in the browser.","commonSituations":"Client lost connectivity mid-session; dev pointing at a localhost server that is not running; slow blob uploads hitting the 15s default because the timeout option wasn't raised; VPN/DNS issues; deploying the web app on a domain not allowed by server CORS.","solutions":["If it's a timeout ('Network error: request timeout'), pass a larger timeout in init (connection.fetch(url, { timeout: 60000 })) or 0 to disable — note blob uploads already set no timeout.","Verify serverBaseUrl is correct and the server is reachable (curl the URL from the same environment).","For offline/CORS: check network connectivity and the server's CORS configuration for the client origin.","Handle it in UI: err instanceof UserFriendlyError && err.code === 'NETWORK_ERROR' → offer retry; keep the original message for diagnostics."],"exampleFix":"// before\nconst res = await connection.fetch('/api/workspaces/' + id + '/blobs/' + key);\n// >15s downloads/reads throw Network error: request timeout\n\n// after\nconst res = await connection.fetch(\n  '/api/workspaces/' + id + '/blobs/' + key,\n  { timeout: 60_000 }\n);","handlingStrategy":"retry","validationCode":"async function reachable(baseUrl: string): Promise<boolean> {\n  try { await fetch(new URL('/api/workspaces', baseUrl), { method: 'HEAD' }); return true; } catch { return false; }\n}","typeGuard":"import { UserFriendlyError } from '@affine/error';\nconst isNetworkError = (e: unknown): e is UserFriendlyError =>\n  e instanceof UserFriendlyError && e.code === 'NETWORK_ERROR';","tryCatchPattern":"async function fetchWithRetry(url: string, init?: RequestInit & { timeout?: number }, tries = 3) {\n  for (let i = 0; ; i++) {\n    try { return await connection.fetch(url, init); }\n    catch (e) {\n      if (i < tries - 1 && e instanceof UserFriendlyError && e.code === 'NETWORK_ERROR') {\n        await new Promise(r => setTimeout(r, 2 ** i * 500)); continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Pass an explicit timeout sized to the operation (uploads/downloads: large or 0)","Check connectivity before long sync sessions and queue work offline","Configure server CORS for the exact client origin","Never retry non-idempotent mutations blindly — only GETs/fetches"],"tags":["network","fetch","timeout","affine"],"backgroundTag":"network-request-failed","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}