# Error dialog

> What went wrong, where, and the traceback, with Copy and Go to source. A Pyzed component for Python desktop GUIs, with examples and its API.

Source: https://albakore.github.io/pyzed/library/error-dialog/

:::tip[In plain words]
A window that pops up when something fails, saying what went wrong and where, with a button to copy it for a bug report.
:::

`ui.error_dialog(info, on_close)` shows an error in a dialog: its message, where it happened, the file and line it comes from and the traceback in a read-only editor, where any part can be selected and copied. Its buttons copy the whole report, go to the origin (`on_go_to`, labeled `go_to_label`), show it among the logs (`on_logs`) and close. `more` adds a line saying how many other errors came after it. `info` is a `ui.ErrorInfo`; show the dialog with a [Dialog layer](https://albakore.github.io/pyzed/library/dialog-layer/).

`ui.error_dialog` · Category: [Dialogs](https://albakore.github.io/pyzed/library/#dialogs) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#error_dialog)

**Code:**

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.dialog_layer(main_view(), ui.error_dialog(info, on_close, on_go_to=on_go_to, on_logs=on_logs, width=620))
```

:::note[New]
This component was added recently. Tell us how it works for you in the [issue tracker](https://github.com/albakore/pyzed/issues).
:::

## When to use

- When something the user asked for fails (saving, exporting, running a script) and the details matter.
- From the Details button of an [Error boundary](https://albakore.github.io/pyzed/library/error-boundary/).
- For a short message without a traceback, use an [Alert](https://albakore.github.io/pyzed/library/alert/) with `kind="error"`.

## Usage

```python
import pyzed as pz
from pyzed import ui

class Notes(pz.App):
    def __init__(self):
        self.error = None

    def save(self):
        try:
            write_notes(self.notes)
        except OSError as err:
            self.error = ui.ErrorInfo.from_exception(err, where="Save")

    def close_error(self):
        self.error = None

    def view(self):
        dialog = ui.error_dialog(self.error, self.close_error) if self.error else None
        return ui.dialog_layer(self.page(), dialog, on_dismiss=self.close_error)
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Options

Without a traceback the dialog leaves out the code box, as with an `ErrorInfo` you build yourself.

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.error_dialog(info, on_close, width=480)
```

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.error_dialog(info, on_close, on_go_to=on_go_to, go_to_label="Open app.py",
                width=480)
```

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.error_dialog(info, on_close, on_logs=on_logs, width=480)
```

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.error_dialog(info, on_close, more=2, width=480)
```

**} alt={**

```python
def average(values):
    return sum(values) / len(values)

try:
    average([])
except ZeroDivisionError as err:
    info = ui.ErrorInfo.from_exception(err, where="Totals")

ui.error_dialog(info, on_close, title="The totals could not be computed", width=480)
```

```python
info = ui.ErrorInfo("PermissionError", "[Errno 13] Permission denied: 'notes.md'", where="Save")
ui.error_dialog(info, on_close, width=480)
```

## Events

Error dialog has **3 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Handler gets | When |
| --- | --- | --- |
| `on_close` | `()` | Close button and Escape. |
| `on_go_to` | `()` | Adds the go-to button (open the file at the line). |
| `on_logs` | `()` | Adds a Show in Logs button. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Notes(pz.App):
    def __init__(self):
        try:
            open("/no/such/file")
        except OSError as err:
            self.error = ui.ErrorInfo.from_exception(err, where="Open")

    def close(self):                     # on_close: the button and Escape
        self.error = None

    def view(self):
        dialog = ui.error_dialog(self.error, self.close,
                                 on_go_to=lambda: print("go to", self.error.origin),  # adds Go to
                                 on_logs=lambda: print("show logs")) if self.error else None
        return ui.dialog_layer(main_view(), dialog, on_dismiss=self.close)
```

**Declarative:**

```python
class Notes(pz.App):
    error = ui.ErrorInfo("FileNotFoundError", "No such file: notes.txt")

    def update(self, message):
        match message:
            case "close":
                self.error = None
            case "logs":
                print("show logs")

    def view(self):
        dialog = ui.error_dialog(self.error, "close", on_logs="logs") if self.error else None
        return ui.dialog_layer(main_view(), dialog, on_dismiss="close")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    error: object = ui.ErrorInfo("FileNotFoundError", "No such file: notes.txt")

def view(state, set_state):
    close = lambda: set_state(error=None)
    dialog = ui.error_dialog(state.error, close) if state.error else None
    return ui.dialog_layer(main_view(), dialog, on_dismiss=close)
```

## Tips

:::tip
`on_go_to` receives no arguments: read the file and line from `info.origin` (a `(path, line)` pair, or `None`) and open it there. The traceback box is a read-only editor, so the user can select a single line and copy it, while Copy error copies `info.report()`.
:::

## Properties and API

### `ui.error_dialog`

```python
error_dialog(
    info,
    on_close,
    *,
    title=None,
    on_go_to=None,
    go_to_label=None,
    on_logs=None,
    more=0,
    width=680,
    theme=None,
)
```

A dialog with an error: its message, where it happened, the file and line it comes from and the traceback in a read-only editor (select and copy any part), with buttons to copy everything, go to the origin, see it among the logs and close. Show it with `ui.dialog_layer` or `ui.layer`.

| Property | Default | Description |
| --- | --- | --- |
| `info` | *required* | A `ui.ErrorInfo`. |
| `title` | `None` | Heading ("Something went wrong"). |
| `go_to_label` | `None` | Its text ("Go to source"). |
| `more` | `0` | How many other errors came after it (a line says so). |
| `width` | `680` | Width in pixels or a modal size. |
| `theme` | `None` | Theme override. |

Events: `on_close`, `on_go_to`, `on_logs` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#error_dialog)

## Related

- [Error boundary](https://albakore.github.io/pyzed/library/error-boundary/): Draws a part of the view, or an error card in its place when it fails.
- [Alert](https://albakore.github.io/pyzed/library/alert/): A message with one button.
- [Dialog layer](https://albakore.github.io/pyzed/library/dialog-layer/): Centers a dialog over your view with a dimmed backdrop.
- [Modal](https://albakore.github.io/pyzed/library/modal/): A dialog surface with title, content and footer buttons.
