louislam/uptime-kuma · info · Error

user not found, have you installed?

Error message

user not found, have you installed?

What it means

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.

Source

Thrown at extra/remove-2fa.js:23

const { R } = require("redbean-node");
const readline = require("readline");
const TwoFA = require("../server/2fa");
const args = require("args-parser")(process.argv);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
});

const main = async () => {
    Database.initDataDir(args);
    await Database.connect();

    try {
        // No need to actually reset the password for testing, just make sure no connection problem. It is ok for now.
        if (!process.env.TEST_BACKEND) {
            const user = await R.findOne("user");
            if (!user) {
                throw new Error("user not found, have you installed?");
            }

            console.log("Found user: " + user.username);

            let ans = await question("Are you sure want to remove 2FA? [y/N]");

            if (ans.toLowerCase() === "y") {
                await TwoFA.disable2FA(user.id);
                console.log("2FA has been removed successfully.");
            }
        }
    } catch (e) {
        console.error("Error: " + e.message);
    }

    await Database.close();
    rl.close();

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Example fix

// before
if (update.channel_post) {
    this.$parent.notification.baleChatID = update.channel_post.chat.id;
} else if (update.message) {
    this.$parent.notification.baleChatID = update.message.chat.id;
} else {
    throw new Error(this.$t("chatIDNotFound"));
}

// after: also accept other update shapes that still carry a chat
let chatId =
    update.channel_post?.chat.id ??
    update.message?.chat.id ??
    update.edited_message?.chat.id ??
    update.my_chat_member?.chat.id ??
    update.callback_query?.message?.chat.id;

if (chatId != null) {
    this.$parent.notification.baleChatID = chatId;
} else {
    throw new Error(this.$t("chatIDNotFound"));
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Before calling axios, confirm a token is set so getUpdates is even valid
if (!this.$parent.notification.baleBotToken || !this.$parent.notification.baleBotToken.trim()) {
    this.$root.toastError(this.$t("baleBotTokenRequired"));
    return;
}

Type guard

// Narrow a raw Bale/Telegram update to one that exposes a chat.id
function hasChatId(update) {
    return Boolean(
        update?.channel_post?.chat?.id ??
        update?.message?.chat?.id ??
        update?.edited_message?.chat?.id ??
        update?.my_chat_member?.chat?.id ??
        update?.callback_query?.message?.chat?.id
    );
}

// usage inside autoGetBaleChatID
const update = res.data.result[res.data.result.length - 1];
if (!hasChatId(update)) {
    throw new Error(this.$t("chatIDNotFound"));
}

Try / catch

// Keep the single try/catch, but classify so the toast is specific
try {
    const res = await axios.get(this.baleGetUpdatesURL("withToken"));
    const update = res.data?.result?.[res.data.result.length - 1];
    if (!hasChatId(update)) throw new Error(this.$t("chatIDNotFound"));
    this.$parent.notification.baleChatID =
        update.channel_post?.chat.id ?? update.message?.chat.id;
} catch (error) {
    // Distinguish network/HTTP errors from the local chatIDNotFound
    const msg = error.response
        ? `${this.$t("chatIDNotFound")} (HTTP ${error.response.status})`
        : error.message;
    this.$root.toastError(msg);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/e104f9fe4e3111a3. Report an issue: GitHub.