Repository: louislam/uptime-kuma
File: server/model/monitor.js (JavaScript)

Code before:
                        {
                            allowAutoTopicCreation: this.kafkaProducerAllowAutoTopicCreation,
                            ssl: this.kafkaProducerSsl,
                            clientId: `Uptime-Kuma/${version}`,
                            interval: this.interval,
                        },
                        JSON.parse(this.kafkaProducerSaslOptions)
                    );
                    bean.status = UP;
                    bean.ping = dayjs().valueOf() - startTime;
                } else {
                    throw new Error("Unknown Monitor Type");
                }

                if (this.isUpsideDown()) {
                    bean.status = flipStatus(bean.status);

                    if (bean.status === DOWN) {
                        throw new Error("Flip UP to DOWN");
                    }
                }

                retries = 0;
            } catch (error) {
                if (error?.name === "CanceledError") {
                    bean.msg = `timeout by AbortSignal (${this.timeout}s)`;
                } else {
                    bean.msg = error.message;
                }

                // If UP come in here, it must be upside down mode
                // Just reset the retries
                if (this.isUpsideDown() && bean.status === UP) {
                    retries = 0;
                } else {
                    // For json-query monitors with retry_only_on_status_code_failure enabled,
                    // only retry if the error is NOT from JSON query evaluation
                    // JSON query errors have the message "JSON query does not pass..."
                    const isJsonQueryError =
                        this.type === "json-query" &&
                        typeof error.message === "string" &&
                        error.message.includes("JSON query does not pass");

                    const shouldSkipRetry = isJsonQueryError && this.retry_only_on_status_code_failure;

                    if (shouldSkipRetry) {
                        // Don't retry on JSON query failures, mark as DOWN immediately
                        retries = 0;
                    } else if (this.maxretries > 0 && retries < this.maxretries) {
                        retries++;
                        bean.status = PENDING;

Review comment (bug):
this change makes the entire system how retries work very obvuscated.

Please add a `this.type === "json-query"` branch where you put all relevant logic instead.
This is likely very bug-prown otherwise.

Code after:
                } else {
                    throw new Error("Unknown Monitor Type");
                }

                if (this.isUpsideDown()) {
                    bean.status = flipStatus(bean.status);

                    if (bean.status === DOWN) {
                        throw new Error("Flip UP to DOWN");
                    }
                }

                retries = 0;
            } catch (error) {
                if (error?.name === "CanceledError") {
                    bean.msg = `timeout by AbortSignal (${this.timeout}s)`;
                } else {
                    bean.msg = error.message;
                }

                // If UP come in here, it must be upside down mode
                // Just reset the retries
                if (this.isUpsideDown() && bean.status === UP) {
                    retries = 0;
                } else if (this.type === "json-query" && this.retry_only_on_status_code_failure) {
                    // For json-query monitors with retry_only_on_status_code_failure enabled,
                    // only retry if the error is NOT from JSON query evaluation
                    // JSON query errors have the message "JSON query does not pass..."
                    const isJsonQueryError =
                        typeof error.message === "string" && error.message.includes("JSON query does not pass");

                    if (isJsonQueryError) {
                        // Don't retry on JSON query failures, mark as DOWN immediately
                        retries = 0;
                    } else if (this.maxretries > 0 && retries < this.maxretries) {
                        retries++;
                        bean.status = PENDING;
                    } else {
                        // Continue counting retries during DOWN
                        retries++;
                    }
                } else {
                    // General retry logic for all other monitor types
                    if (this.maxretries > 0 && retries < this.maxretries) {
                        retries++;
                        bean.status = PENDING;
                    } else {
                        // Continue counting retries during DOWN
                        retries++;
                    }
                }