{"record":{"id":"e104f9fe4e3111a3","repo":"louislam/uptime-kuma","slug":"user-not-found-have-you-installed","errorCode":null,"errorMessage":"user not found, have you installed?","messagePattern":"user not found, have you installed\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"extra/remove-2fa.js","lineNumber":23,"sourceCode":"const { R } = require(\"redbean-node\");\nconst readline = require(\"readline\");\nconst TwoFA = require(\"../server/2fa\");\nconst args = require(\"args-parser\")(process.argv);\nconst rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout,\n});\n\nconst main = async () => {\n    Database.initDataDir(args);\n    await Database.connect();\n\n    try {\n        // No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.\n        if (!process.env.TEST_BACKEND) {\n            const user = await R.findOne(\"user\");\n            if (!user) {\n                throw new Error(\"user not found, have you installed?\");\n            }\n\n            console.log(\"Found user: \" + user.username);\n\n            let ans = await question(\"Are you sure want to remove 2FA? [y/N]\");\n\n            if (ans.toLowerCase() === \"y\") {\n                await TwoFA.disable2FA(user.id);\n                console.log(\"2FA has been removed successfully.\");\n            }\n        }\n    } catch (e) {\n        console.error(\"Error: \" + e.message);\n    }\n\n    await Database.close();\n    rl.close();\n","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/extra/remove-2fa.js#L5-L41","documentation":"Thrown locally by autoGetBaleChatID (Bale.vue:97) when the newest entry returned by Bale's bot getUpdates endpoint is an update type the code does not recognize. The handler only reads update.channel_post and update.message; any other update shape sitting on top of the result stack (callback_query, edited_message, my_chat_member, poll, chat_member, etc.) makes both branches miss and the code throws this Error. It is immediately caught and shown as a red toast, so it is a controlled user-facing message rather than a crash.","triggerScenarios":"Clicking the 'Auto Get' button next to bale-chat-id after the bot's last received update was NOT a plain message or channel post. Concretely: a user pressed an inline button (callback_query), edited or forwarded an earlier message (edited_message), the bot was added/removed from a chat (my_chat_member / chat_member), or a poll-related update landed last. Each leaves res.data.result populated but the top item has no .channel_post and no .message key, so the else branch at line 97 fires.","commonSituations":"Bot freshly added to a group (Bale emits my_chat_member), which then sits above the real /start message. A user editing a previously sent message pushes an edited_message update on top. A conflicting webhook or a prior getUpdates call with offset already consumed the real messages, leaving only member/poll updates. Long-running bots whose update queue has accreted non-message events.","solutions":["Send a brand-new plain message (or /start) to the bot or target channel in Bale, wait a moment, then click 'Auto Get' again so a message/channel_post update is the newest one.","If the queue is polluted, clear it by calling getUpdates with an offset past the last update_id (or delete and recreate the webhook), then retry Auto Get.","Make the handler tolerant: also read chat.id from edited_message.chat.id, my_chat_member.chat.id, and callback_query.message.chat.id so non-message updates still yield an ID.","Scan res.data.result from the newest entry backwards and pick the first update that carries any recognizable chat object, instead of only inspecting the single last entry."],"exampleFix":"// before\nif (update.channel_post) {\n    this.$parent.notification.baleChatID = update.channel_post.chat.id;\n} else if (update.message) {\n    this.$parent.notification.baleChatID = update.message.chat.id;\n} else {\n    throw new Error(this.$t(\"chatIDNotFound\"));\n}\n\n// after: also accept other update shapes that still carry a chat\nlet chatId =\n    update.channel_post?.chat.id ??\n    update.message?.chat.id ??\n    update.edited_message?.chat.id ??\n    update.my_chat_member?.chat.id ??\n    update.callback_query?.message?.chat.id;\n\nif (chatId != null) {\n    this.$parent.notification.baleChatID = chatId;\n} else {\n    throw new Error(this.$t(\"chatIDNotFound\"));\n}","handlingStrategy":"type-guard","validationCode":"// Before calling axios, confirm a token is set so getUpdates is even valid\nif (!this.$parent.notification.baleBotToken || !this.$parent.notification.baleBotToken.trim()) {\n    this.$root.toastError(this.$t(\"baleBotTokenRequired\"));\n    return;\n}","typeGuard":"// Narrow a raw Bale/Telegram update to one that exposes a chat.id\nfunction hasChatId(update) {\n    return Boolean(\n        update?.channel_post?.chat?.id ??\n        update?.message?.chat?.id ??\n        update?.edited_message?.chat?.id ??\n        update?.my_chat_member?.chat?.id ??\n        update?.callback_query?.message?.chat?.id\n    );\n}\n\n// usage inside autoGetBaleChatID\nconst update = res.data.result[res.data.result.length - 1];\nif (!hasChatId(update)) {\n    throw new Error(this.$t(\"chatIDNotFound\"));\n}","tryCatchPattern":"// Keep the single try/catch, but classify so the toast is specific\ntry {\n    const res = await axios.get(this.baleGetUpdatesURL(\"withToken\"));\n    const update = res.data?.result?.[res.data.result.length - 1];\n    if (!hasChatId(update)) throw new Error(this.$t(\"chatIDNotFound\"));\n    this.$parent.notification.baleChatID =\n        update.channel_post?.chat.id ?? update.message?.chat.id;\n} catch (error) {\n    // Distinguish network/HTTP errors from the local chatIDNotFound\n    const msg = error.response\n        ? `${this.$t(\"chatIDNotFound\")} (HTTP ${error.response.status})`\n        : error.message;\n    this.$root.toastError(msg);\n}","preventionTips":["Send a fresh message to the bot immediately before clicking 'Auto Get' so a message/channel_post is guaranteed to be the newest update.","Drain the bot's update queue (getUpdates with offset, or deleteWebhook) before running auto-detect in a CI/test environment.","Never run auto-detect while a webhook is registered for the same bot token.","Extend hasChatId-style narrowing before reading update fields so unhandled update types degrade gracefully instead of throwing."],"tags":["bale","bot-api","getupdates","vue","notification-provider","user-facing"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}