I have published a GitHub repository:
Achronym “SERE”
I would use an achronym “SERE” (Stale Element Reference Exception) in this article for short.
Problem to solve
In the Katalon Community, there are a lot of topics about SERE:
-
Unable to catch StaleElementReferenceException using try/catch mehcanism
-
Katalon Studio Fails to Handle StaleElementReferenceException
-
Ways to fix the StaleElementReferenceExcetpion (and similar exception)
-
Headless Chrome Browser - getting StaleElementReferenceException for some elements
Please make a search in the forum to look up more
The posters were eager to find out some way to fix/manage/avoid SERE but they failed. Most of these topics are still open (unresolved) today.
I found a common shortcoming in these posts about SERE. The original posters ask for help for fixing (avoiding) SERE in their own projects, but they do not provide any sample code in Katalon Studio that enable you to reproduce the SERE in your hand. With no codes shared, the discussions stayed ambiguous resulting no idea what to do next.
Reference
In order to understand what Stale Element Reference Exception is, I refered to the article:
Solution
In this project, I would show you several test scripts in Katalon Studio. With these sample scripts, you can firmly reproduce SERE on your machine. The scripts are short. If you read the codes carefully, you would understand how a StaleElementReferenceException is thrown.
Decription
The target page
At first, I created a HTML file as the target of my tests. The file located at <ProjectDir>/docs/targetPage.html
.
You can see the HTML in action at:
When the page opens, it will look like this:
3 seconds after open, the <button id='myButton'>
element is silently removed. And the button is recreated soon. The content text and the style is slightly changed, but the id
value remains the same: myButton
.
The HTML contains the following JavaScript. This changes the DOM dynamically.
function modifyPage() {
// remove the <button @id='myButton'>
document.getElementById('myButton').remove();
// recreate the <button @id='byButton'>
let btn = document.createElement('button');
btn.id = 'myButton';
btn.textContent = 'This button once was removed and recreated'
btn.classList = ['recreated'];
document.getElementById('main').append(btn);
console.log('modified the page')
}
// will modify the page at 3 seconds after the initial loading
window.addEventListener("load", (event) => {
console.log('the page was loaded initially');
const timeout = window.setTimeout(modifyPage, 3000);
console.log('timeout was set');
});
</script>
</body>
Object Repository/myButton
I created a Test Object named myButton
, which contains a simple XPath expression:
//button[@id='myButton']
This XPath expression will select the button in the target page. The <button id='myButton'>
keeps the id
value unchanged before and after the DOM modification by JavaScript. Therefore the same xpath applies.
Test Cases/TC1
See the source of
Test Cases/TC1.
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import java.nio.file.Path
import java.nio.file.Paths
import org.openqa.selenium.WebElement
import com.kms.katalon.core.configuration.RunConfiguration
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
/**
* TC1
*
* This script can reproduce a Selenium StaleElementReferenceException (SERE).
* The target HTML is dynamically modifiedy by JavaScript inside it.
* An HTML node will be removed and recreated at 3 seconds after the initial page load.
* A reference to the HTML node as org.openqa.selenium.WebElement object will
* get stale by the DOM modification by JavaScript.
*
* Referring to the stale WebElement object will cause a SERE.
*
* @author kazurayam
*/
// identify the location of target HTML file
Path projectDir = Paths.get(RunConfiguration.getProjectDir())
Path html = projectDir.resolve("docs/targetPage.html")
URL htmlURL = html.toFile().toURI().toURL()
String urlString = htmlURL.toExternalForm()
WebUI.comment("navigating to " + urlString)
// open a browser, navigate to the target page
WebUI.openBrowser('')
WebUI.setViewPortSize(800, 600)
WebUI.navigateToUrl(urlString)
TestObject myButtonTestObject = findTestObject("Object Repository/myButton")
// make sure <button id='myButton'> is displayed in the page initially
WebUI.verifyElementPresent(myButtonTestObject, 10, FailureHandling.STOP_ON_FAILURE)
// get the reference to the HTML element <button id='myButton'>
WebElement myButtonWebElement = WebUI.findWebElement(myButtonTestObject, 10, FailureHandling.STOP_ON_FAILURE)
// the test intentionally does nothing for long enough seconds
WebUI.delay(5)
// At 3 seconds after the page load, JavaScript wil remove and recreate the HTML element
try {
// at 3 secs after the initial page loading,
// the old <button id='myButton'> is removed, then
// a new <button id='myButton'> is recreted.
myButtonWebElement.click() // this statement will throw a StaleElementReferenceException
} catch (Exception e) {
WebUI.comment(">>> An Exception was caught: " + e.getClass().getName() + ": " + e.getMessage() + " <<<")
}
//WebUI.closeBrowser()
The TC1 does the following steps:
-
open browser, navigate to the targetPage.html, prepare a Test Object that selects the
<button id='myButton'>
element, makes sure the page is opened. -
TC1 creates a variable named
myButtonWebElement
which is type oforg.openqa.selenium.WebElement
, refers to the<button id='myButton'>
element in the HTML DOM. -
TC1 intentionally waits for 5 seconds.
-
On the other hand, in the opened browser, at 3 seconds after the page load, the
<button id='myButton'>
in blue color is removed. Then a new<button id='myButton'>
in grey color is created. -
After 5 seconds of wait, TC1 calls
myButtonWebElement.click()
. At this call, aStaleElementReferenceException
will be thrown.
See the console log emited by TC1:
12月 05, 2024 9:52:44 午後 com.kms.katalon.core.logging.KeywordLogger startTest
情報: START Test Cases/TC1
...
12月 05, 2024 9:52:59 午後 com.kms.katalon.core.logging.KeywordLogger logInfo
情報: An Exception was caught: org.openqa.selenium.StaleElementReferenceException: stale element reference: stale element not found in the current frame
(Session info: chrome=131.0.6778.109)
For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#stale-element-reference-exception
Build info: version: '4.22.0', revision: 'c5f3146703'
System info: os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '14.7.1', java.version: '17.0.7'
Driver info: com.kms.katalon.selenium.driver.CChromeDriver
Command: [ac30658fb877503145f65a505a4902e5, clickElement {id=f.D3000530D4F4A376D08D574B93D847AB.d.2AC594D17950A4F164BD8602A5C7E00D.e.3}]
Capabilities {acceptInsecureCerts: false, browserName: chrome, browserVersion: 131.0.6778.109, chrome: {chromedriverVersion: 131.0.6778.87 (ce31cae94873..., userDataDir: /var/folders/7m/lm7d6nx51kj...}, fedcm:accounts: true, goog:chromeOptions: {debuggerAddress: localhost:63200}, networkConnectionEnabled: false, pageLoadStrategy: normal, platformName: mac, proxy: Proxy(), se:cdp: ws://localhost:63200/devtoo..., se:cdpVersion: 131.0.6778.109, setWindowRect: true, strictFileInteractability: false, timeouts: {implicit: 0, pageLoad: 300000, script: 30000}, unhandledPromptBehavior: ignore, webSocketUrl: ws://localhost:45754/sessio..., webauthn:extension:credBlob: true, webauthn:extension:largeBlob: true, webauthn:extension:minPinLength: true, webauthn:extension:prf: true, webauthn:virtualAuthenticators: true}
Element: [[CChromeDriver: chrome on mac (ac30658fb877503145f65a505a4902e5)] -> xpath: //button[@id='myButton']]
Session ID: ac30658fb877503145f65a505a4902e5 <<<
12月 05, 2024 9:52:59 午後 com.kms.katalon.core.logging.KeywordLogger endTest
情報: END Test Cases/TC1
The variable myButtonWebElement
is an instance of org.openqa.selenium.WebElement
class. Initially, the variable got a valid reference to the <button id='myButton'>
element in the target page. But after 5 seconds of wait, the reference becomes stale because the JavaScript in the target page dynamically changed the page’s DOM. This is the core reason why a StaleElementReferenceException
is thrown.
Test Cases/TC2
Let me show you another sample code. The TC1 referred to a variable of type org.openqa.selenium.WebElement
class. But a usual Katalon Studio user won’t use the Selenium WebDriver API. They would primarily use WebUI.*
keywords. The next TC2 calls only WebUI
keywords, and it stil reproduces a StaleElementReferenceException.
See the source of Test Cases/TC2.
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import java.nio.file.Path
import java.nio.file.Paths
import org.openqa.selenium.WebElement
import com.kms.katalon.core.configuration.RunConfiguration
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
/**
* TC2
*
* This script demonstrates the WebUI.waitForElementNotClickable keyword throws
* Selenium StaleElementReferenceException (SERE).
*
* @author kazurayam
*/
Path projectDir = Paths.get(RunConfiguration.getProjectDir())
Path html = projectDir.resolve("docs/targetPage.html")
URL htmlURL = html.toFile().toURI().toURL()
String urlString = htmlURL.toExternalForm()
WebUI.comment("navigating to " + urlString)
// open a browser, navigate to the target web page
WebUI.openBrowser('')
WebUI.navigateToUrl(urlString)
WebUI.setViewPortSize(800, 600)
TestObject myButtonTestObject = findTestObject("Object Repository/myButton")
// make sure <button id='myButton'> is displayed in the page initially
WebUI.verifyElementPresent(myButtonTestObject, 10, FailureHandling.STOP_ON_FAILURE)
try {
// at 3 secs after the initial page loading,
// the old <button id='myButton'> was removed, but soon
// a new <button id='myButton'> was recreated.
// The keyword will see the HTML node stays clickable untile the timeout expires
// However, the keyword throws a SERE for some reason.
// Do you know why?
WebUI.waitForElementNotClickable(myButtonTestObject,
10,
FailureHandling.STOP_ON_FAILURE)
} catch (Exception e) {
println ">>> An Exception was caught: " + e.getClass().getName() + ": " + e.getMessage() + " <<<"
println "==========================================================================="
e.printStackTrace()
println "==========================================================================="
}
WebUI.closeBrowser()
When I ran the TC2
, I saw the following messages in the console. You see, a SERE was thrown.
12月 05, 2024 10:01:44 午後 com.kms.katalon.core.logging.KeywordLogger startTest
情報: START Test Cases/TC2
...
12月 05, 2024 10:01:54 午後 com.kms.katalon.core.logging.KeywordLogger logFailed
重大: ❌ Unable to wait for object 'Object Repository/myButton' to be not clickable (Root cause: com.kms.katalon.core.exception.StepFailedException: Unable to wait for object 'Object Repository/myButton' to be not clickable
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain.stepFailed(WebUIKeywordMain.groovy:117)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain.runKeyword(WebUIKeywordMain.groovy:43)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain$runKeyword.call(Unknown Source)
at com.kms.katalon.core.webui.keyword.builtin.WaitForElementNotClickableKeyword.waitForElementNotClickable(WaitForElementNotClickableKeyword.groovy:108)
at com.kms.katalon.core.webui.keyword.builtin.WaitForElementNotClickableKeyword.execute(WaitForElementNotClickableKeyword.groovy:69)
at com.kms.katalon.core.keyword.internal.KeywordExecutor.executeKeywordForPlatform(KeywordExecutor.groovy:74)
at com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords.waitForElementNotClickable(WebUiBuiltInKeywords.groovy:597)
at TC2.run(TC2:47)
...
Caused by: org.openqa.selenium.StaleElementReferenceException: stale element reference: stale element not found in the current frame
(Session info: chrome=131.0.6778.109)
...
==========================================================================
>>> An Exception was caught: com.kms.katalon.core.exception.StepFailedException: Unable to wait for object 'Object Repository/myButton' to be not clickable
===========================================================================
12月 05, 2024 10:01:55 午後 com.kms.katalon.core.logging.KeywordLogger endTest
情報: END Test Cases/TC2
Please find that the Exception was raised at (WaitForElementNotClickableKeyword.groovy:108)
. So we should check the source code of Katalon Studio at com.kms.katalon.core.webui.keyword.builtin.WaitForElementNotClickableKeyword
package com.kms.katalon.core.webui.keyword.builtin
...
public class WaitForElementNotClickableKeyword extends WebUIAbstractKeyword {
..
public boolean waitForElementNotClickable(TestObject to, int timeOut, FailureHandling flowControl) throws StepFailedException {
return WebUIKeywordMain.runKeyword({
...
try {
...
try {
...
WebElement foundElement = WebUIAbstractKeyword.findWebElement(to, timeOut) // Line#103
WebDriverWait wait = new WebDriverWait(DriverFactory.getWebDriver(), Duration.ofSeconds(timeOut))
foundElement = wait.until(new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
if (foundElement.isEnabled()) { // Line#108
return null
} else {
return foundElement
}
}
})
...
return true
} catch (WebElementNotFoundException e) {
...
return false
} catch (TimeoutException e) {
...
return false
}
} finally {
...
}
}, ...)
}
}
Why a StaleElementReferenceException was thrown at the Line#108?
At the Line#103, a variable named foundElement
is declared to have a reference to an org.openqa.selenium.WebElement
object, which points to the <button id='myButton'>
element in the target page.
At the Line#108, the WebUI.waitForElementNotClickable
keyword repeats referring to the foundElement
until it find the web element is not clickable any more.
While the keyword is in the loop, in the target web page, the initial <button id='myButton'>
element is once removed; and a new <button id='myButton'>
element will be inserted.
At this timing, the foundElement
becomes stale. It is not referring to a valid HTML element any more.
Therefore the WebUI.waitForElementNotClickable
keyword threws a StaleElementReferenceException. A SERA was thrown by TC2 by just the same reason as the TC1.
Which WebUI keywords are likely to throw SERE?
In the TC2, I pointed out that the WebUI.waitForElementNotClickable
keyword is likely to throw a StaleElementReferenceException.
Any other keywords would behave the same?
In the Test Cases/TC5, I pick up just a few other WebUI keywords and found the following 2 built-in keywords threw SERE.
-
WebUI.waitForElementNotHasAttribute
-
WebUI.waitForElementNotVisible
There could be more.
It was interesting to find that the WebUI.waitForElementNotPresent
keyword did not throw SERE. I checked its Groovy source code and found it is implemented nicely so that it prevents SERE.
Stateful keywords, Stateless keywords
I would like to propose a set of categorical terms:
-
Stateless keyword, e.g,
WebUI.waitForElementNotPresent
-
Stateful keyword, e.g,
WebUI.waitForElementNotClickable
A stateful keyword has a variable of type org.openqa.selenium.WebElement
, which is once gets initialized with a valid reference to a DOM element
in the target page, and is kept in memory and is referred to repeatedly for long seconds.
When the DOM is changed by JavaScript in the target page, the variable becomes stale. A stateful keyword is unreliable. Occationally it could throw a StaleElementReferenceException.
A stateless keyword doesn’t have such a variable, therefore won’t throw any StaleElementReferenceException.
I believe that all built-in keywords should be stateless so that they never throw StaleElementReferenceException.
So, how many stateful keywords are there in Katalon Studio? — Well, I don’t know. I hope that Katalon will publish the list of stateful keywords A.S.A.P. so that the users can refrain from those risky keywords.
Test Cases/TC3
I would show one more script. The TC3 is almost the same as TC2 except one line different. See the source of Test Cases/TC3.
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import java.nio.file.Path
import java.nio.file.Paths
import org.openqa.selenium.WebElement
import com.kms.katalon.core.configuration.RunConfiguration
import com.kms.katalon.core.model.FailureHandling
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
/**
* TC3
*
* A variation derived from the TC2.
*
* This script demonstrates that FailureHandling.CONTINUE_ON_FAILURE makes
* all keywords silent.
* No exception will be raised by a keyword invokation.
* Your Test Case script can not catch any Exception.
*
* @author kazurayam
*/
Path projectDir = Paths.get(RunConfiguration.getProjectDir())
Path html = projectDir.resolve("docs/targetPage.html")
URL htmlURL = html.toFile().toURI().toURL()
String urlString = htmlURL.toExternalForm()
WebUI.comment("navigating to " + urlString)
WebUI.openBrowser('')
WebUI.navigateToUrl(urlString)
WebUI.setViewPortSize(800, 600)
TestObject myButtonTestObject = findTestObject("Object Repository/myButton")
WebUI.verifyElementPresent(myButtonTestObject, 10, FailureHandling.STOP_ON_FAILURE)
try {
WebUI.verifyElementNotPresent(myButtonTestObject,
10,
FailureHandling.CONTINUE_ON_FAILURE)
// The keyword will throw no Exception
} catch (Exception e) {
// You can not catch SERE here
println ">>> An Exception was caught: " + e.getClass().getName() + ": " + e.getMessage() + " <<<"
println "==========================================================================="
e.printStackTrace()
println "==========================================================================="
}
WebUI.closeBrowser()
// In the end, in the Console, you will find a long Stack trace of StepFailedException is printed.
The TC2 has a code fragmen like this:
try {
WebUI.waitForElementNotClickable(myButtonTestObject,
10,
FailureHandling.STOP_ON_FAILURE)
// so the keyword will throw a SERE
The TC3 has a code like this:
try {
WebUI.waitForElementNotClickable(myButtonTestObject,
10,
FailureHandling.CONTINUE_ON_FAILURE)
} catch (Exception e) {
// You can not catch SERE here
println ">>> An Exception was caught: " + e.getClass().getName() + ": " + e.getMessage() + " <<<"
println "==========================================================================="
e.printStackTrace()
println "==========================================================================="
}
When I ran the TC3, I got the following output in the console:
12月 05, 2024 11:29:53 午後 com.kms.katalon.core.logging.KeywordLogger startTest
情報: START Test Cases/TC3
...
12月 05, 2024 11:30:24 午後 com.kms.katalon.core.logging.KeywordLogger logFailed
重大: ❌ Web element with id: 'Object Repository/myButton' located by 'By.xpath: //button[@id='myButton']' is present after '10' second(s) (Root cause: com.kms.katalon.core.exception.StepFailedException: Web element with id: 'Object Repository/myButton' located by 'By.xpath: //button[@id='myButton']' is present after '10' second(s)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain.stepFailed(WebUIKeywordMain.groovy:117)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain$stepFailed$0.call(Unknown Source)
at com.kms.katalon.core.webui.keyword.builtin.VerifyElementNotPresentKeyword$_verifyElementNotPresent_closure1.doCall(VerifyElementNotPresentKeyword.groovy:124)
at com.kms.katalon.core.webui.keyword.builtin.VerifyElementNotPresentKeyword$_verifyElementNotPresent_closure1.doCall(VerifyElementNotPresentKeyword.groovy)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain.runKeyword(WebUIKeywordMain.groovy:35)
at com.kms.katalon.core.webui.keyword.internal.WebUIKeywordMain$runKeyword.call(Unknown Source)
at com.kms.katalon.core.webui.keyword.builtin.VerifyElementNotPresentKeyword.verifyElementNotPresent(VerifyElementNotPresentKeyword.groovy:133)
at com.kms.katalon.core.webui.keyword.builtin.VerifyElementNotPresentKeyword.execute(VerifyElementNotPresentKeyword.groovy:70)
at com.kms.katalon.core.keyword.internal.KeywordExecutor.executeKeywordForPlatform(KeywordExecutor.groovy:74)
at com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords.verifyElementNotPresent(WebUiBuiltInKeywords.groovy:1557)
at TC3.run(TC3:40)
...
Caused by: com.kms.katalon.core.exception.StepFailedException: Web element with id: 'Object Repository/myButton' located by 'By.xpath: //button[@id='myButton']' is present after '10' second(s)
... 27 more
...
12月 05, 2024 11:30:25 午後 com.kms.katalon.core.logging.KeywordLogger endTest
情報: END Test Cases/TC3
You can see there is no output from the statement
println ">>> An Exception was caught: " + e.getClass().getName() + ": " + e.getMessage() + " <<<"
This implies that no exception was thrown out of a keyword call because FailureHandling.CONTINUE_ON_FAILURE
was specified.
Test Cases/TC4, custom resolution
The TC2 demonstrates that the WebUI.waitForElementNotClickable
keyword occasionally throws a StaleElementReferenceException.
Can I fix this problematic keyword? — Yes, I can propose an idea. Let me show it.
I made 2 codes:
The TC4 is similar to the TC2 but different. The TC4 calls com.kazurayam.hack.StatelessWaitForElementNotClickableKeyword
class instead of the WebUI.waitForElementNotClickable
keyword. When I ran the TC4, it threws no SERE.
Let’s compare the source of 2 classes to see the difference:
com.kms.katalon.core.webui.keyword.builtin.WaitForElementNotClickable
This code is likely to throw SERE.
...
try {
WebElement foundElement = WebUIAbstractKeyword.findWebElement(to, timeOut)
WebDriverWait wait = new WebDriverWait(DriverFactory.getWebDriver(), Duration.ofSeconds(timeOut))
foundElement = wait.until(new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
if (foundElement.isEnabled()) {
return null
} else {
return foundElement
}
}
})
if (foundElement != null) {
...
}
return true
...
com.kazurayam.hack.StatelessWaitForElementNotClickable
This code does not throw any SERE.
...
try {
WebDriverWait wait = new WebDriverWait(DriverFactory.getWebDriver(), Duration.ofSeconds(timeOut))
WebElement webElement = wait.until(new ExpectedCondition<WebElement>() {
@Override
public WebElement apply(WebDriver driver) {
// recreate a reference to the <button id='myBook'>
WebElement foundElement = WebUIAbstractKeyword.findWebElement(to, timeOut)
if (foundElement.isEnabled()) {
return null
} else {
return foundElement
}
}
})
...
In the built-in keyword, the foundElement
variable is created once outside the scope of wait
loop and refered to many times inside the wait
loop. The reference to the <button id='myButton'>
element has enough chance to get stale.
On the other hand, in my class, the foundElement
variable is scoped narrow; it resides inside the apply
method. The foundElement
variable is created once, refered to only once, then immediately thrown away. The reference to the <button id='myButton'>
has no chance to get stale.
TC6 WebUI.waitForElementClickable keyword could throw a StaleElementReferenceException
The TC2 showed me there are some problematic keywords with name starting with “WebUI.waitForElementNot”. But how about the keywords of “WebUI.waitForElement*” ? No one throws a SERE? I examined some and found that “WebUI.waitForElementClickable” keyword could throw a SERE.
I made a Test Case:
This script targets the url
When I ran the TC6, I saw a SERE was thrown:
Why the waitForElementClickable
keyword threw a StaleElementReferenceException? There is a pitfall in the target HTML. The HTML initially has a <button id="myButton" disabled>
.
The disabled
attribute makes the element unable to click. Therefore the WebUI.waitForElementClickable
keyword is forced to wait for a while. Then at 3 secs after the initial page loading, JavaScript removes the element and recreate a new <button id="myButton">
with no disabled
attribute. As soon as the button element is recreated, the WebUI.waitForElementClickable
keyword tries to access to the WebElement object, which has got already stale.
The WebUI.waitForElementClickable
is a stateful keyword. There could be more.
Conclusion
I presented the reason how a StaleElementReferenceException is raised by Katlaon WebUI keywords. The Exception could be thrown with the combination of 2 factors:
-
the target web page is driven by JavaScript, which changes the page’s DOM dynamically: remove an Element, recreate the Element.
-
the WebUI keyword is implemented stateful like
WebUI.waitForElementNotClickable
. A variable of typeorg.openqa.selenium.WebElement
is repeatedly referred to while theWebElement
turned to be stale due to the dynamic DOM change by JavaScript in the target web page.
Lesson learned:
-
when you encountered a SERE, you need to study how your target web page is written. You need to be aware how JavaScript works inside the page.
-
you need to read the source code of WebUI keywords if it threw a SERE.
How can you avoid SERE at all? Well, I don’t know. There is no silver bullet. Please find your way for yourself.
In the TC4, I showed how to fix the defect in a built-in keyword. I hope Katalon to address my suggestion and fix the problematic built-in keywords.