# Unknown outcomes

Source: https://phonebox.dev/docs/troubleshooting/unknown-outcomes

> What action_outcome_unknown means, why Phonebox never retries a write for you, and how to observe and decide what to do next.



`action_outcome_unknown` means Phonebox sent a write to the phone, such as a tap, typing or an install, and the phone didn't confirm it. The request may have timed out, or the phone's connection broke off after the write could already have reached it. The action may have happened, partly happened, or not happened at all. Its `retryable` is always `false`.

## Where it appears [#where-it-appears]

* **In an action's result.** The batch stops at that action, and the actions after it don't run. The response includes the element list of the screen, unless reading the screen after the batch failed or ran out of time.
* **As the error of a whole request** that writes to the phone: installing or uninstalling an app, uploading or deleting a file, setting the clipboard, setting or resetting the location, setting the locale or the timezone, and rebooting. These answer `502 action_outcome_unknown`.

A partial action is a related case. A `type` with `submit` and a `double_tap` each take two steps, and when the second step fails after the first reached the phone, the result has `details.partial` set to `true` and `retryable` set to `false`, whatever its code. The first step happened, and the second may or may not have.

## Why nothing is retried for you [#why-nothing-is-retried-for-you]

Phonebox sends every write to the phone once and never repeats it, and the SDK and the CLI don't either. A repeated tap on "Pay" can pay twice, a repeated "Send" can send a message twice, and a repeated reboot restarts a phone that may already be restarting. Only you can tell from the screen whether the first attempt worked.

Reads are different, because repeating them changes nothing: Phonebox retries its own reads of the phone for up to about 15 seconds. And when Phonebox's own error says that nothing ran, such as a retryable `503 phone_unavailable` in its [error envelope](/docs/api-reference/errors), the write never reached the phone, so sending it again is safe.

## Observe, then decide [#observe-then-decide]

1. **Observe.** Read the screen, with a screenshot when the element list might not show the change: `GET /v1/phones/{id}/observe?screenshot=jpeg&max_width=720`. If the phone was rebooting, wait for it with `GET /v1/phones/{id}?wait=ready` first.
2. **Compare.** Work out what the action should have changed. Look for the next screen, the text in the field, the item in the cart, or the message in the conversation.
3. **Decide.**
   * If it happened, carry on with the next step, as if the action had succeeded.
   * If it clearly didn't happen, send it again, once.
   * If you can't tell, don't guess. Check in a way that is safe to repeat, such as a `wait_for` on the text that would confirm it, or ask your user, handing the phone over with a [live view link](/docs/using-phones/live-view) when a person should look.

For a partial action, observe in the same way, and never send the second step blindly. For a `type` with `submit`, send a `key` action for `enter` only when the screen still shows your text in the field and hasn't moved on. For a `double_tap`, check whether the second tap is still needed before you tap again.

## Checks for requests that write [#checks-for-requests-that-write]

| Request                                                  | How to check whether it happened                                                                                                                                               |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Install an app                                           | [List the installs](/docs/api-reference/apps#list-app-installs), or the installed apps.                                                                                        |
| Uninstall an app                                         | [List the apps](/docs/api-reference/apps#list-apps).                                                                                                                           |
| Upload or delete a file                                  | [List the directory](/docs/api-reference/files#list-files).                                                                                                                    |
| Set the clipboard                                        | [Read it back](/docs/api-reference/device#read-the-clipboard).                                                                                                                 |
| Set or reset the location, or set the locale or timezone | These settings can't be read back, but sending the same value again leaves the phone as you meant it. A locale change restarts the interface each time, so observe afterwards. |
| Reboot                                                   | Wait with `GET /v1/phones/{id}?wait=ready`, as the error's `next` says. Never send a second reboot to find out whether the first one worked.                                   |

## When no answer comes from Phonebox [#when-no-answer-comes-from-phonebox]

A timeout or a dropped connection on your side hides the outcome in the same way, even when Phonebox finished the request. So does a `502` or `504` whose body isn't Phonebox's error envelope, which a proxy, a load balancer or the hosting platform sends, for example when a request runs past its time limit. None of these says whether anything ran. Treat them by what the request does:

* **Create.** Send it again with the same `Idempotency-Key` and body. You get the phone the first request created, if it did, and never a second one. If that phone has failed or was deleted, create the next one with a new key. Without a key, list your phones before you create another.
* **Park and heartbeat.** Send them again. They are safe to repeat.
* **Start.** Read the phone first, with `GET /v1/phones/{id}`. A start on a running phone renews its session and reserves credit again, so start it again only while it is still `parked` or `unavailable`. If it is `starting` or `ready`, it is running: wait with `GET /v1/phones/{id}?wait=ready` instead.
* **An action batch.** Observe first, as above. Give batch requests a client timeout of about 300 seconds, so that a long batch isn't cut off on your side.
* **Other writes.** Check whether they happened, with the [checks above](/docs/troubleshooting/unknown-outcomes#checks-for-requests-that-write), before you send them again.
* **Reads.** Send them again.
