How to Retry Failed iOS Tests with Xcode on App Automate
Retry failed XCUITest cases on BrowserStack App Automate with the right Xcode settings, API request, XCTestPlan mode, and failure diagnostics.
To retry failed XCUITest cases on BrowserStack App Automate, submit the XCUITest build with retryTestsOnFailure: true and a testIterations value from 1 to 5. This API retry option requires iOS 15 or later and has no effect when singleRunnerInvocation is true. For an xctestrun build, you can instead configure test repetition in an XCTestPlan.
A retry automates another attempt; it does not repair the underlying cause. App Automate reports the status of the last attempt, so inspect the failed attempt’s logs even when a later attempt passes. See BrowserStack’s retry guide and XCTestPlan guidance.
1. Check the build and device requirements
- Use an App Automate XCUITest build submitted through the XCUITest builds API.
- For API retry fields, select iOS 15 or later. BrowserStack documents that
retryTestsOnFailureandtestIterationsare not supported on earlier iOS versions. - Leave
singleRunnerInvocationfalse or omit it. False is the default. With a single runner invocation enabled, all test cases run together in one process, so BrowserStack cannot isolate and rerun an individual failed case. - Confirm that your app and test suite have already been uploaded and that their references are valid for the build request.
2. Submit a build with failed-test retries
The endpoint is POST https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build. Authenticate with your BrowserStack username and access key using HTTP Basic authentication. Set both retry fields: the Boolean enables retries, and the integer sets the maximum number of attempts for a failing case, from 1 through 5.
curl -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \
-X POST "https://api-cloud.browserstack.com/app-automate/xcuitest/v2/build" \
-H "Content-Type: application/json" \
-d '{
"devices": ["iPhone 13-15"],
"app": "<app_url>",
"testSuite": "<test_suite_url>",
"retryTestsOnFailure": true,
"testIterations": 3
}'
Replace the example device selection and uploaded app and suite references with values for your account and target run. The value 3 is an example, not a universal recommendation. Choose a maximum that fits your debugging and runtime budget. The official guide maps these fields to Xcode’s -retry-tests-on-failure and -test-iterations options.
What the retry fields mean
| Field | Type | Behavior |
|---|---|---|
retryTestsOnFailure |
Boolean | When true, a failed test case is retried. Attempts stop when it passes or reaches the configured iteration count. |
testIterations |
Integer | Maximum number of times a specific unsuccessful case runs. Must be greater than 0 and no more than 5. |
singleRunnerInvocation |
Boolean | Must not be true for these per-case retry parameters to take effect. False is the default. |
testRelaunchEnabled |
Boolean | BrowserStack recommends considering this alongside retry and iterations for the listed runner communication and test process crash errors; it is not a general fix for every failure. |
Optional runner relaunch for specified process errors
If the run reports Failed to establish communication with the test runner or Test crashed with signal trap/abrt/kill, BrowserStack documents testRelaunchEnabled for use with retry and iteration settings. Add it to the JSON body for those cases:
{
"retryTestsOnFailure": true,
"testIterations": 3,
"testRelaunchEnabled": true
}
Keep the rest of the build request fields, such as devices, app and testSuite. Only enable relaunch when the runner or process error makes it relevant.
3. Choose retry behavior for an XCTestPlan
If you submit an xctestrun build with an XCTestPlan, configure the plan’s Test Repetition Mode. The available choices have different semantics:
| Mode | Documented behavior | Consider when |
|---|---|---|
| Retry on Failure | Runs the test again where failure occurred. | You want a failed case to get another attempt. |
| Up Until Maximum Repetitions | Repeats according to the configured Maximum Test Repetitions value. | You explicitly want a fixed repetition limit. |
| Until Failure | Continues executing test cases until a failure occurs. | You want repetition to stop at the first failure. |
| None | Does not rerun test cases after failure. | You want a single attempt. |
These are XCTestPlan settings for the xctestrun workflow, not alternative names for the API’s testIterations field. Match the setting to the build input and desired repetition behavior. Consult BrowserStack’s XCTestPlan instructions for where to set the plan flag.
4. Read the result as a sequence of attempts
For a case run multiple times, the status displayed in the App Automate dashboard or API is the last attempt’s status. A final pass can therefore coexist with a meaningful earlier failure. Treat a retry pass as evidence that the failure did not reproduce on that attempt, not proof that a deterministic app defect is fixed.
- Identify which case failed first and whether it later passed or failed again.
- Compare instrumentation logs for the attempts. Look for a repeatable assertion or app behavior versus a runner, device, or communication failure.
- Use device and network logs when those logging options were enabled for the run.
- For a process crash, retrieve the crash report where available. BrowserStack describes crash logs as OS-generated iOS reports.
- Download the result bundle when needed and inspect it in an Xcode-compatible editor.
The XCUITest session API reference documents per-test status and debugging fields, including instrumentation logs, device logs, network logs, crash logs, and result bundles. Device and network logs depend on enabling their corresponding parameters.
5. Troubleshoot retries that do not happen
| Symptom | Likely cause | Fix |
|---|---|---|
| Failed cases only run once on iOS 14 or earlier. | API retry is unsupported on iOS versions earlier than 15. | Use an iOS 15 or later target for this API retry workflow. |
| Retry fields appear to be ignored. | singleRunnerInvocation is true. |
Set it to false or omit it so cases can run separately. |
| Build submission is rejected. | Malformed JSON, invalid uploaded app or suite reference, or incorrect authentication. | Check the response, ensure valid JSON and references, and verify the username and access key. |
testIterations is rejected. |
The value is zero, negative, non-integer, or greater than five. | Use an integer from 1 through 5. |
| The build succeeds but a case remains failed. | The case failed on every permitted attempt, or has a deterministic failure. | Inspect each attempt’s artifacts and fix the cause; retries cannot make a failing assertion pass reliably. |
| Dashboard shows a pass despite an earlier failure. | For repeated cases, displayed status reflects the last attempt. | Review the earlier attempt’s instrumentation and device artifacts before treating the test as healthy. |
| Runner communication or signal crash errors continue. | The test runner process may need a separate relaunch. | For the documented error scenarios, use testRelaunchEnabled with retry fields, then inspect crash and runner logs. |
| No device or network logs are available. | The corresponding logging parameters were not enabled. | Enable those options on a future run if those artifacts are needed. |
6. Budget runtime and preserve signal
A maximum of five iterations bounds repeated work, but flaky cases can still lengthen a run. Use a modest limit while diagnosing a suite, then adjust based on how often transient failures occur and how much additional runtime the team can accept. Each retry consumes time and can delay feedback; a green final status should not be used to conceal recurring failures.
Retries are most useful when failures are intermittent and the extra attempt helps distinguish transient conditions from repeatable defects. Track which cases need retries and examine earlier attempts. A test that repeatedly fails before passing deserves investigation even if the build’s final status is green.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It is useful when your workflow also needs web page screenshots; it does not submit or retry XCUITest builds. A single GET request captures a URL:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
See the ScreenshotNeo API documentation for the request options. Cookie banners, newsletter popups and chat widgets are removed before the screenshot; bot checks, blank pages and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.
FAQ
Does a retry rerun the entire XCUITest suite?
The API retry setting applies to failed test cases, which BrowserStack can isolate when singleRunnerInvocation is false.
Does a later pass mean the bug is fixed?
No. It means the last attempt passed. Check the earlier failure artifacts and establish whether the cause was transient or reproducible.
Can I set more than five API iterations?
No. BrowserStack requires testIterations to be greater than zero and at most five.
Can I use the API retry fields on older iOS versions?
Not on iOS versions earlier than 15, according to BrowserStack’s current retry documentation.


