{"record":{"id":"2975ecf47cb27f36","repo":"RocketChat/Rocket.Chat","slug":"failed-to-get-apps-status-from-node-nodeid","errorCode":null,"errorMessage":"Failed to get apps status from node ${nodeID}","messagePattern":"Failed to get apps status from node (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/services/apps-engine/service.ts","lineNumber":234,"sourceCode":"\t\t// We can filter out the local node because we already know its status\n\t\tconst availableNodes = services?.find((service) => service.name === 'apps-engine')?.nodes;\n\n\t\t// Subtract 1 for the local node\n\t\tif (!availableNodes || availableNodes.length - 1 < 1) {\n\t\t\tthrow new AppsEngineNoNodesFoundError();\n\t\t}\n\n\t\tconst statusByApp: AppStatusReport = {};\n\n\t\tconst apps: Promise<void>[] = availableNodes.map(async (nodeID) => {\n\t\t\tconst appsStatus: Awaited<ReturnType<typeof this.getAppsStatusLocal>> | undefined = await this.api?.call(\n\t\t\t\t'apps-engine.getAppsStatusLocal',\n\t\t\t\t[],\n\t\t\t\t{ nodeID },\n\t\t\t);\n\n\t\t\tif (!appsStatus) {\n\t\t\t\tthrow new Error(`Failed to get apps status from node ${nodeID}`);\n\t\t\t}\n\n\t\t\tappsStatus.forEach(({ status, appId }) => {\n\t\t\t\tif (!statusByApp[appId]) {\n\t\t\t\t\tstatusByApp[appId] = [];\n\t\t\t\t}\n\n\t\t\t\tstatusByApp[appId].push({ instanceId: nodeID, isLocal: nodeID === localNodeId, status });\n\t\t\t});\n\t\t});\n\n\t\tawait Promise.all(apps);\n\n\t\treturn statusByApp;\n\t}\n}\n","sourceCodeStart":216,"sourceCodeEnd":251,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/services/apps-engine/service.ts#L216-L251","documentation":"Thrown while aggregating app status in the apps-engine service: the RPC call 'apps-engine.getAppsStatusLocal' to a workspace node returned undefined. In multi-node deployments the local node fans out to every available node; any node that fails to answer (or answers with nothing) produces this per-node Error, which fails the whole Promise.all aggregation.","triggerScenarios":"Any node in the cluster being down or restarted during the status sweep; the apps-engine microservice unreachable from the calling node; API timeouts returning undefined rather than a payload.","commonSituations":"Rolling restarts where one node is briefly absent; network partitions between workspace nodes; apps-engine service crashed on a remote node while the orchestrator still lists it as available.","solutions":["Check the health of every node listed by the node registry (availableNodes) and restart unhealthy ones","Verify the apps-engine service is running and reachable on the failing nodeID","Retry the status call after the node recovers"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const healthy = await Promise.all(availableNodes.map((id) => api.call('apps-engine.getAppsStatusLocal', [], { nodeID: id }).then(Boolean).catch(() => false)));\nif (healthy.some((ok) => !ok)) { /* skip or report degraded before aggregating */ }","typeGuard":"function isAppsStatusReport(v: unknown): v is AppStatusReport {\n  return Array.isArray(v) && v.every((e) => e && typeof e.appId === 'string' && typeof e.status !== 'undefined');\n}","tryCatchPattern":"try { await service.getAppsStatus(); } catch (e) { if (e.message.startsWith('Failed to get apps status from node')) { /* wait for node recovery, then retry the sweep */ } }","preventionTips":["Run health checks on nodes before fanning out status calls","Drain nodes from the registry before restarting them"],"tags":["apps-engine","multi-node","rpc","cluster","rocket-chat"],"backgroundTag":"service-call-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}