{"record":{"id":"3524e64db00d4f7d","repo":"RocketChat/Rocket.Chat","slug":"invalid-response-from-api","errorCode":null,"errorMessage":"Invalid response from API","messagePattern":"Invalid response from API","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/client/apps/orchestrator.ts","lineNumber":56,"sourceCode":"\t}\n\n\tpublic handleError(error: unknown): void {\n\t\tif (hasAtLeastOnePermission(['manage-apps'])) {\n\t\t\tdispatchToastMessage({\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: error,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic async getInstalledApps(): Promise<App[]> {\n\t\tconst result = await sdk.rest.get<'/apps/installed'>('/apps/installed', { includeClusterStatus: 'true' });\n\n\t\tif ('apps' in result) {\n\t\t\t// TODO: chapter day: multiple results are returned, but we only need one\n\t\t\treturn result.apps as App[];\n\t\t}\n\t\tthrow new Error('Invalid response from API');\n\t}\n\n\tpublic async getAppsFromMarketplace(isAdminUser?: boolean): Promise<{ apps: App[]; error?: unknown }> {\n\t\tlet result: App[] = [];\n\t\ttry {\n\t\t\tresult = await sdk.rest.get('/apps/marketplace', { isAdminUser: isAdminUser ? isAdminUser.toString() : 'false' });\n\t\t} catch (e) {\n\t\t\tif (isErrorObject(e)) {\n\t\t\t\treturn { apps: [], error: e.error };\n\t\t\t}\n\t\t\tif (typeof e === 'string') {\n\t\t\t\treturn { apps: [], error: e };\n\t\t\t}\n\t\t}\n\n\t\tif (!Array.isArray(result)) {\n\t\t\t// TODO: chapter day: multiple results are returned, but we only need one\n\t\t\treturn { apps: [], error: 'Invalid response from API' };","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/client/apps/orchestrator.ts#L38-L74","documentation":"Thrown by AppClientOrchestrator.getInstalledApps after a GET /apps/installed?includeClusterStatus=true whose response body does not contain an `apps` key. The method uses a runtime structural check ('apps' in result) because the SDK typing cannot guarantee the shape; a missing key means the server returned something unexpected (an error object, an empty 200, or a different schema version).","triggerScenarios":"The marketplace/apps engine is disabled on the server so /apps/installed returns a non-standard body; the server returned an error envelope (e.g. { error: '...' }) that still resolved with HTTP 200; an older/newer server version where the response key differs; the request was intercepted by a proxy that replaced the body.","commonSituations":"Apps-engine not enabled in deployment; version mismatch between client bundle and server (e.g. client newer than server); a gateway/auth layer returning a JSON error page with 200 status; the apps-engine microservice migration producing a transitional response shape.","solutions":["Open DevTools Network and inspect the raw /apps/installed response body — a missing `apps` key is the defect to fix.","Confirm the Apps subsystem is enabled on the server (Administration > Apps) and that the user has 'manage-apps' permission.","Verify server and client versions are compatible; a much older server may not return the expected envelope.","If a proxy is in front, ensure it does not rewrite 200 responses for the /apps/ path.","Report the actual returned shape to harden the structural check or update the rest-typings contract."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasAppsKey(r: unknown): r is { apps: App[] } {\n  return typeof r === 'object' && r !== null && 'apps' in r;\n}","tryCatchPattern":"try {\n  const apps = await orchestrator.getInstalledApps();\n} catch (e) {\n  if ((e as Error).message === 'Invalid response from API') {\n    // inspect network response; likely apps-engine disabled or version mismatch\n  }\n}","preventionTips":["Verify the apps engine is enabled and the user has 'manage-apps' permission.","Keep client and server versions compatible.","Ensure proxies do not rewrite 200 responses on the /apps/ path."],"tags":["apps-engine","marketplace","rest","response-shape"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}