{"record":{"id":"1eb8359402076a64","repo":"openclaw/openclaw","slug":"telegram-runtime-unavailable-runtime-keys-runt","errorCode":null,"errorMessage":"telegram runtime unavailable (runtime keys: ${runtimeKeys.join(\",\")}; channel keys: ${channelKeys.join(\",\")})","messagePattern":"telegram runtime unavailable \\(runtime keys: (.+?); channel keys: (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"extensions/device-pair/index.ts","lineNumber":982,"sourceCode":"        const payload = await issueSetupPayload({\n          url: urlResult.url,\n          urls: urlResult.urls,\n          allowFullAccess: authState.canIssueFullAccessSetup,\n        });\n\n        if (channel === \"telegram\" && target) {\n          try {\n            const runtimeKeys = Object.keys(api.runtime ?? {});\n            const channelKeys = Object.keys(api.runtime?.channel ?? {});\n            api.logger.debug?.(\n              `device-pair: runtime keys=${runtimeKeys.join(\",\") || \"none\"} channel keys=${\n                channelKeys.join(\",\") || \"none\"\n              }`,\n            );\n            const adapter = await api.runtime.channel.outbound.loadAdapter(\"telegram\");\n            const send = adapter?.sendText;\n            if (!send) {\n              throw new Error(\n                `telegram runtime unavailable (runtime keys: ${runtimeKeys.join(\",\")}; channel keys: ${channelKeys.join(\n                  \",\",\n                )})`,\n              );\n            }\n            await send({\n              cfg: api.config,\n              to: target,\n              text: formatSetupInstructions(payload.expiresAtMs),\n              ...(ctx.messageThreadId != null ? { threadId: ctx.messageThreadId } : {}),\n              ...(ctx.accountId ? { accountId: ctx.accountId } : {}),\n            });\n            api.logger.info?.(\n              `device-pair: telegram split send ok target=${target} account=${ctx.accountId ?? \"none\"} thread=${\n                ctx.messageThreadId ?? \"none\"\n              }`,\n            );\n            return { text: encodeSetupCode(payload) };","sourceCodeStart":964,"sourceCodeEnd":1000,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/device-pair/index.ts#L964-L1000","documentation":"Thrown in the device-pair setup command (extensions/device-pair/index.ts:982) when the channel is telegram and a target exists, the plugin loads the telegram outbound adapter via api.runtime.channel.outbound.loadAdapter('telegram'), but the resulting adapter has no sendText method. This path attempts to split the QR-image delivery from the setup code across two messages. The throw is immediately caught by the surrounding try/catch (line 1001) which logs a warning and falls back to a single combined text message — so the user still receives a setup code. The error message includes runtime/channel key listings purely for diagnostics.","triggerScenarios":"The telegram outbound adapter fails to load, loads as undefined, or loads but exposes no sendText function — while a telegram setup command is being handled with a valid target recipient.","commonSituations":"Telegram channel plugin not installed or enabled; runtime version missing the channel.outbound.loadAdapter surface; adapter version mismatch where sendText was renamed/removed; telegram bot not configured so the adapter is inert; partial plugin load after a config reload.","solutions":["No user action required in most cases — the command falls back to a single-message setup code automatically.","If split telegram delivery (image + code) is desired, ensure the telegram channel plugin is installed, enabled, and configured with a valid bot token.","Inspect the device-pair debug log line ('device-pair: runtime keys=... channel keys=...') to see which runtime/channel surfaces are missing.","Run 'openclaw doctor' to verify telegram channel health."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasTelegramSendText(api: unknown): boolean {\n  const runtime = (api as { runtime?: { channel?: { outbound?: { loadAdapter?: unknown } } } })?.runtime;\n  return !!runtime?.channel?.outbound && typeof runtime.channel.outbound.loadAdapter === \"function\";\n}","tryCatchPattern":"// The throw is already caught internally at index.ts:1001 and degrades to a\n// single-message reply. Callers need no extra handling — this is informational.","preventionTips":["This error is caught internally and degrades gracefully; no caller action needed.","Ensure the telegram plugin is installed and configured if split delivery matters.","Check the device-pair debug log for missing runtime/channel surfaces."],"tags":["device-pair","telegram","channel-adapter","runtime","fallback"],"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}