{"record":{"id":"9b18245366f1f946","repo":"infiniflow/ragflow","slug":"request-failed","errorCode":null,"errorMessage":"Request failed","messagePattern":"Request failed","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/services/skill-space-service.ts","lineNumber":134,"sourceCode":"}\n\nclass SkillSpaceService {\n  private async request<T>(\n    method: string,\n    url: string,\n    data?: any,\n    params?: any,\n  ): Promise<T> {\n    const response: any = await request(url, {\n      method: method as any,\n      data,\n      params,\n    });\n\n    const jsonData = response?.data ?? response;\n\n    if (jsonData?.code !== 0) {\n      throw new Error(jsonData?.message || 'Request failed');\n    }\n\n    return jsonData.data;\n  }\n\n  // ==================== Skill Space Management ====================\n\n  // List all skill spaces\n  async listSpaces(): Promise<{ spaces: SkillSpace[]; total: number }> {\n    return await this.request<{ spaces: SkillSpace[]; total: number }>(\n      'GET',\n      api.skillSpaces,\n    );\n  }\n\n  // Create a new skill space\n  async createSpace(request: CreateSpaceRequest): Promise<SkillSpace> {\n    return await this.request<SkillSpace>('POST', api.skillSpaces, request);","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/web/src/services/skill-space-service.ts#L116-L152","documentation":"This error is thrown by the SkillSpaceService.request wrapper in web/src/services/skill-space-service.ts:134 whenever the backend responds with a JSON body whose `code` field is anything other than 0. The wrapper normalizes every skill-space endpoint (listSpaces, createSpace, getSpace, etc.) through one path, so a non-zero code from ANY of these APIs surfaces as 'Request failed' when the response omits a `message` field. It is not a network error; the HTTP request completed and returned an application-level failure envelope.","triggerScenarios":"Any SkillSpaceService call (GET api.skillSpaces, POST createSpace, GET/PUT/DELETE api.skillSpace(spaceId), skill indexing) where the server returns {code: != 0} with an empty or missing `message` — e.g. server-side validation failure, duplicate space name, or a backend exception handler that returns code 109/500 without a message. Also triggered when the response shape is unexpected (jsonData is null/undefined after `response?.data ?? response`), making `code` undefined !== 0.","commonSituations":"Backend returns a raw string or HTML error page (proxy 502/504) that axios wraps so jsonData has no `code`; API base URL points to the wrong server; backend version mismatch where new error codes lack messages; expired auth token causing a redirect/HTML login page instead of JSON.","solutions":["Check the browser Network tab for the actual response body of the failing skill-space request to see the real code and any message","If the response is not JSON (HTML error page, empty body), fix the server/proxy issue — the wrapper misclassifies it as an app error","Improve the wrapper to include `code` in the thrown message (e.g. `Request failed (code ${jsonData?.code})`) so failures are diagnosable","Verify the session token is valid and the request reached the RAGFlow API server rather than a gateway error page"],"exampleFix":"// before\nif (jsonData?.code !== 0) {\n  throw new Error(jsonData?.message || 'Request failed');\n}\n\n// after\nif (jsonData?.code !== 0) {\n  const detail = jsonData?.message || 'Request failed';\n  throw new Error(`${detail} (code: ${jsonData?.code ?? 'no-code'})`);\n}","handlingStrategy":"try-catch","validationCode":"const isValidEnvelope = (d: unknown): d is { code: number; data: unknown; message?: string } =>\n  typeof d === 'object' && d !== null && 'code' in d;\n\n// before calling a skill-space mutation:\nif (!spaceName?.trim()) {\n  throw new Error('Space name is required'); // prevent obvious server-side rejections\n}","typeGuard":"function isSkillSpaceEnvelope(v: any): v is { code: number; message?: string; data: any } {\n  return typeof v?.code === 'number';\n}","tryCatchPattern":"try {\n  const spaces = await skillSpaceService.listSpaces();\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (/code:\\s*(?!0)/.test(msg)) {\n    // application-level rejection — surface message to user, do not retry\n    notification.error({ description: msg });\n  } else {\n    throw e; // transport/proxy issue — let React Query retry\n  }\n}","preventionTips":["Always pass server-validated fields (non-empty names, valid IDs) to skill-space calls to avoid code != 0 rejections","Keep the axios interceptor and this wrapper aligned on the response envelope shape","Include the numeric code in thrown messages so failures are diagnosable in logs"],"tags":["api","response-envelope","skill-space","typescript","frontend"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}