Katalon keeps failing on 2FA screen — how to pause test for manual OTP entry?

Hey everyone, I’m really stuck here. I’m trying to automate our company’s login flow using Katalon Studio, but our staging environment just forced 2FA (Two-Factor Authentication).

Every time I run the script, it hits the ‘Enter Your OTP’ screen. I tried using the regular WebUI.delay(30) to give myself 30 seconds to look at my phone and type the code in manually. But sometimes the SMS takes too long, or I mistype it, and Katalon just blindly jumps to the next step and clicks the submit button anyway! Then the test crashes because the field is empty or the code was wrong.

I also tried WebUI.waitForElementPresent, but that doesn’t stop the script from failing after the timeout finishes. I just want Katalon to literally freeze, wait for me to type the OTP into the browser manually, click ‘Verify’, and only then continue running the rest of the automated test. Is there a way to make Katalon wait for me?"

The Core Issue

Using hardcoded delays (WebUI.delay) or standard element waits (WebUI.waitForElementPresent) fails in this scenario because they operate on absolute timers. They do not actually listen to the state of your application or wait for human interaction to complete.

The Industry-Standard Solution

To gracefully handle manual intervention during automated test execution (a practice often used in semi-automated testing or debugging environments), you should leverage Conditional Synchronization Hooks.

Instead of waiting for a set amount of time, we configure Katalon to poll the browser until a specific post-login element appears (like a “Dashboard” header) or until the 2FA input field disappears from the DOM.

However, the cleanest and most robust method for manual overrides is to use Java’s JOptionPane. This pops up a native UI dialog box that completely halts the Katalon execution thread. The automation will sit paused indefinitely until you type the OTP into the popup and hit “OK”, at which point Katalon injects your input into the browser and proceeds.

The Code Implementation

Since this is a reusable utility that you’ll likely need across multiple login test cases, the best practice in Katalon Studio is to wrap this logic inside a Custom Keyword.

Step 1: Create the Custom Keyword

In Katalon Studio, navigate to Keywords (in the Object Repository tree), create a new Package (e.g., com.utils), and create a new Keyword class named ManualTestingHelper. Paste the following code:

Groovy

package com.utils

import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.testobject.TestObject
import javax.swing.JOptionPane

public class ManualTestingHelper {

    /**
     * Pauses the test execution and displays a popup dialog for the user to input the OTP.
     * It then automatically types that OTP into the specified test object.
     * * @param to The Test Object representing the OTP input field on the web page.
     */
    @Keyword
    def handleManualOTP(TestObject to) {
        // 1. Bring up a native Java dialog box that pauses the script execution thread
        String userOTP = JOptionPane.showInputDialog(null, "The automation is paused.\nPlease enter the OTP from your device:", "Manual 2FA Verification", JOptionPane.QUESTION_MESSAGE)
        
        // 2. Check if the user didn't cancel or leave it empty
        if (userOTP != null && !userOTP.isEmpty()) {
            // 3. Wait for the browser field to be ready, then send the keys
            WebUI.waitForElementVisible(to, 10)
            WebUI.setText(to, userOTP)
        } else {
            throw new com.kms.katalon.core.exception.StepErrorException("Test aborted or empty OTP provided in the manual popup.")
        }
    }
}

Step 2: How to call it in your Test Case (Script View)

In your actual Test Case script, replace your old delay logic with a call to this custom keyword. Pass it your OTP text box test object:

Groovy

import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

WebUI.openBrowser('https://example.com/login')
// ... navigate and enter username/password ...
WebUI.click(findTestObject('Object Repository/Login_Page/btn_Submit'))

// --- 2FA PAGE REACHED ---
// Call your custom keyword to pause the test and handle the manual input
CustomKeywords.'com.utils.ManualTestingHelper.handleManualOTP'(findTestObject('Object Repository/Login_Page/input_OTP_Field'))

// Continue with the rest of your automation seamlessly
WebUI.click(findTestObject('Object Repository/Login_Page/btn_VerifyOTP'))
WebUI.waitForPageLoad(10)

Why this is better:

  • Flake-Free: It completely eliminates timing out because the Java JOptionPane will hold the execution state indefinitely until a human interacts with it.

  • Separation of Concerns: Your test scripts stay clean, while the logic for handling manual overrides is safely abstracted into your Keywords library.

Did you want to explore how to fully automate this via an API or database query to bypass the manual step entirely down the road?

i will check if API is needed, can we send API call within UI automation ?

yes, search for API automation and write logic based on your workflow.

Does the MFA come only via SMS?

If it is time based OTP for MFA you can check out here how exactly I have implemented in my current project.

hi @dgreen

just use WebUI.waitForElementPresent on an element that only appears after successful verification, like a dashboard header, with a long timeout. Katalon will poll until it shows up, giving you as long as you need to type the OTP manually in the browser.

WebUI.waitForElementPresent(findTestObject('Dashboard/header'), 120) // 2 min window

if you prefer a dialog popup that halts execution completely until you enter the code, use JOptionPane:

import javax.swing.JOptionPane

String otp = JOptionPane.showInputDialog(null, "Enter OTP", "2FA Pause", JOptionPane.QUESTION_MESSAGE)
if (otp) {
    WebUI.setText(findTestObject('Login/input_OTP'), otp)
    WebUI.click(findTestObject('Login/btn_Verify'))
} else {
    WebUI.comment("No OTP entered, stopping test")
    return
}

the popup approach freezes the thread indefinitely so there is no timeout risk. The waitForElementPresent approach is simpler but requires you to type directly into the browser instead of the popup. Either way, avoid WebUI.delay for this since it cannot adapt to how fast you actually complete the step

wouldn’t recommend to pause the test for manual entry for OTP. rather authenticate the MFA via API calls. retrieve OTP from API & then enter otp in UI

seems tough!

can we automate it ?

i extracted otp from email if its same thing i can share my code

Why you want to stop the automation and manually put the access code, better automate the scenario.

I have not done for SMS, but I automated fetching out access code from outlook mailbox using Microsoft Graph API method.

Yes, this will work