> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Engine, Updates & Diagnostics

> Read the startup pill, view engine logs, and fix common failures

The startup pill and engine log tell you exactly what the local engine is doing, and every failure attaches the engine's own output.

<Tip>
  Have a shell? `praisonai-desktop doctor` prints the installed and required `praisonaiagents` in one line and exits non-zero when they're out of sync. See [Headless CLI](/docs/features/desktop/cli).
</Tip>

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
)
# If this agent's engine can't start, the pill shows why.
agent.start("Are you there?")
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Start[⏳ starting engine] --> Ready[✅ engine :PORT]
    Start --> Failed[🛑 engine failed]
    Failed --> Log[📜 Engine Log]

    classDef start fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ready fill:#10B981,stroke:#7C90A0,color:#fff
    classDef failed fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef log fill:#6366F1,stroke:#7C90A0,color:#fff

    class Start start
    class Ready ready
    class Failed failed
    class Log log
```

## Quick Start

<Steps>
  <Step title="Read the startup pill">
    The pill shows `starting engine`, then `engine :PORT` on success or `engine failed` with a tail on failure.
  </Step>

  <Step title="Open the engine log">
    The log viewer shows the engine's recent activity — a bounded 400-line ring buffer — without leaving the app.
  </Step>

  <Step title="Reset the engine if it's stuck">
    Close the app, delete the lockfile in the data directory, and relaunch.
  </Step>
</Steps>

***

## Startup States

| Pill                | Meaning                                                                                                                                                                                                                          |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `starting engine`   | The shell is spawning Python                                                                                                                                                                                                     |
| `Engine: updating…` | The venv's package set drifted from the current requirement — the app is re-running the install step before starting the engine. Only shown while an install is actually running, and restored to `Engine: starting…` on return. |
| `engine :PORT`      | The engine is listening and passed the `/health` probe                                                                                                                                                                           |
| `engine failed`     | Startup failed — the last lines of the engine's output are shown                                                                                                                                                                 |

On failure, the tail comes from the supervisor's 12-line buffer, so you see the actual error rather than a bare exit code.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Shell as 🖥️ Shell
    participant Py as 🧠 Engine

    Shell->>Py: spawn (30s deadline)
    alt announces + health ok
        Py-->>Shell: engine :PORT
    else dies or times out
        Py-->>Shell: engine failed (tail)
    end
```

***

## Startup Log (Breadcrumb)

Every launch writes one line to a small log file in the OS temp directory before the window is built, so a launch is always traceable even when no window appears.

### Where the file lives

| Platform | Path                            |
| -------- | ------------------------------- |
| Windows  | `%TEMP%\PraisonAI-startup.log`  |
| macOS    | `$TMPDIR/PraisonAI-startup.log` |
| Linux    | `/tmp/PraisonAI-startup.log`    |

The temp directory is chosen on purpose: it exists before `%APPDATA%\PraisonAI` is created, so the log is available even on a first launch where the data directory has not yet been created. Creation and provisioning are distinct moments now: the breadcrumb is written in `main()`, before `setup()` creates the folder, so the temp log is always the earlier signal.

### Line format

Each launch appends one tab-separated line:

```
<unix_seconds>	<platform>	<primary|secondary>	pid=<pid>
```

A real primary launch writes two lines with the same pid — `secondary` first (before the process knows it is first), then `primary`:

```
1700000000	windows	secondary	pid=4242
1700000000	windows	primary	pid=4242
```

| Field                   | Meaning                                                                           |
| ----------------------- | --------------------------------------------------------------------------------- |
| `unix_seconds`          | Wall-clock timestamp of the launch — lines sort chronologically                   |
| `platform`              | One of `windows`, `macos`, `linux`                                                |
| `primary` / `secondary` | Whether this launch became the primary shell or was handed off to an existing one |
| `pid=<pid>`             | Process id of the launch itself                                                   |

### Reading the log to diagnose a silent launch

Open the file and read the latest line for the launch you just started.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start[👤 Launched the app<br/>but no window appeared] --> Open[📂 Open startup log]
    Open --> Check{Latest line?}
    Check -->|primary pid=N| A[✅ Primary is alive<br/>likely hidden to tray — click the tray icon]
    Check -->|secondary pid=N only| B[✅ Handed off to primary<br/>ExitCode=0 is normal — look for the tray icon]
    Check -->|no line at all| C[🛑 The shell died before starting<br/>attach the engine log to a bug report]

    classDef q fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff

    class Start,Check q
    class A,B ok
    class C bad
```

A launch that was handed off to an existing primary writes only its own `secondary` line and exits with code `0` — that is expected behaviour, not a crash.

The data folder is a second signal alongside the log line. It is created as soon as the primary reaches `setup()`, so a `primary` line with **no** data folder points at a permissions or disk problem on the folder's parent — the app prints `[praisonai] could not create <path>: <error>` to stderr and keeps going. A missing folder after a `secondary`-only launch is normal: a handed-off launch creates nothing.

<Note>
  The log is best-effort. If the temp directory cannot be written, the app still starts — a shell must never fail to start because it could not write a log. If the file is missing after a launch, the write itself failed; treat that as a permission or disk issue on the temp directory.
</Note>

***

## Common Failures

<AccordionGroup>
  <Accordion title="The Default model picker suggested ids the app cannot run (pre-4756)">
    On builds before [PraisonAI#4756](https://github.com/MervinPraison/PraisonAI/pull/4756) the Default model combobox suggested four ids the shipped app cannot run out of the box: `claude-sonnet-4-20250514`, `claude-opus-4-20250514`, `gemini-2.0-flash`, and `ollama/llama3.2`.

    * Bare Anthropic/Google ids without a **Base URL** take the OpenAI path and are sent to `api.openai.com` carrying your OpenAI key, so they fail with an OpenAI error that names `platform.openai.com`.
    * `ollama/llama3.2` is a **slashed** id, and since [PraisonAI#4722](https://github.com/MervinPraison/PraisonAI/pull/4722) the shipped desktop venv refuses slashed ids (no `litellm`).

    Upgrade to a build that includes #4756 — the picker no longer offers these. To use one of these providers, set a **Base URL** (Settings → Models → `base_url`) to the provider's OpenAI-compatible endpoint and type its **bare** model id in the combobox. Both fields are **Requires restart**. See [Models & API Keys → Using another provider](/docs/features/desktop/models#using-another-provider-anthropic-google-ollama-).
  </Accordion>

  <Accordion title="Setup wizard was invisible behind a restored Train view on first run (pre-4471)">
    On a first launch where the saved view was **Train**, the desktop shell restored that view synchronously before the async engine health check finished. When the health check then resolved to `setup needed` or `engine failed`, the setup wizard and the failure banner rendered into `#thread` — which the Train view had already hidden behind `body.training #thread { display: none }`. The title bar read `setup needed`, the Train form filled the screen, and there was **no visible way to start setup**. This was the reported Windows dead end in [PraisonAI #4441](https://github.com/MervinPraison/PraisonAI/issues/4441).

    Fixed in PraisonAI [#4471](https://github.com/MervinPraison/PraisonAI/pull/4471). `showView(name, {persist=true}={})` grew a `persist` option; a new `showEngineGate()` calls `showView('chat', {persist: false})`; `boot()` runs the gate before both the setup-needed and failed branches. The forced Chat switch is not persisted, so the user's deliberate Train choice returns once the engine is up. If you saw the wizard vanish behind Train after upgrading or on a clean install, upgrade to this release.

    A follow-up in PraisonAI [#4623](https://github.com/MervinPraison/PraisonAI/pull/4623) closes the last rough edge: the temporary Chat switch is no longer stuck on the screen until the app is relaunched. `boot()` now calls a `restoreView()` helper right after the engine finishes provisioning, which re-reads `localStorage.getItem('view')` and re-applies it — so the moment you click **Get started** and setup succeeds, your saved Train view returns in the same session.

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Restore[💾 Restore Train view] --> ThreadHidden[❌ #thread hidden by body.training]
        Boot[⏳ Async engine check] --> NeedsSetup{setup needed?}
        NeedsSetup -->|pre-4471| Buried[🛑 firstRun() renders into hidden #thread]
        NeedsSetup -->|post-4471| Gate[🔧 showEngineGate → Chat, not persisted]
        Gate --> Visible[✅ Wizard visible; Train returns as soon as setup completes]

        classDef in fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef mid fill:#189AB4,stroke:#7C90A0,color:#fff
        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

        class Restore,Boot in
        class NeedsSetup,Gate mid
        class ThreadHidden,Buried bad
        class Visible ok
    ```
  </Accordion>

  <Accordion title="The app opened but the data folder does not exist">
    The data folder is created as soon as the app opens, so a missing folder after a launch means the shell never got that far. Check the [startup log](#startup-log-breadcrumb): a `primary` line with no data folder points at a permissions or disk problem on the folder's parent (the app prints `[praisonai] could not create <path>: <error>` to stderr and keeps going, so the window still opens). Only a `secondary` line means this launch handed off to an already-running app, which does not create anything.
  </Accordion>

  <Accordion title="Right-click or an error dialog doesn't appear (older builds)">
    On earlier builds of the Desktop app, the underlying WKWebView shipped with no JS dialog panel, so `window.prompt()` returned `null` and `window.alert()` did nothing. This made three actions look broken:

    * Right-click on a conversation → **Move to project** issued no request.
    * Adding an MCP server with a blank name silently did nothing.
    * An engine rejection when adding an MCP server showed no error.

    If you see any of these on an old build, upgrade to a build that includes PraisonAI [#4521](https://github.com/MervinPraison/PraisonAI/pull/4521). Current builds use in-app dialogs that render regardless of what the WebView's native dialog panel supports. See the [Move to project](/docs/features/desktop/conversations) flow and the MCP [Add errors](/docs/features/desktop/mcp#add-errors) behaviour for what the fixed dialogs look like.
  </Accordion>

  <Accordion title="The tools ran, but no answer came back">
    Kind `no_answer`. The tools succeeded and their results are shown above the banner, but the model produced no follow-up answer for them.

    From `praisonaiagents 1.7.2` onward the library raises loudly instead of ending the stream silently, so this banner is the only user-visible sign. Retry the turn — the tool result is already in the agent's history, so the retry often answers in plain text.

    Requires `praisonaiagents>=1.7.2` in the engine venv. From PraisonAI [#4670](https://github.com/MervinPraison/PraisonAI/pull/4670) onward, the desktop app compares the shipped requirement against a stamp inside the venv on every launch and re-runs the install once when they differ — so an install stuck on 1.7.1 is brought to the current floor automatically the next time you open the app (the tray reads `Engine: updating…` while it runs). If you are on a build before that fix and see `"the engine produced no output"` on every tool call, upgrade the desktop app: pre-4670 builds froze the venv at whatever the first run installed.
  </Accordion>

  <Accordion title="engine failed: missing dependency">
    A killed dependency install can leave a complete-but-empty venv, so the engine dies with `ModuleNotFoundError` / `ImportError`. The startup failure now offers **Set up the environment** and **Copy details** buttons and treats an import error as a setup problem rather than an unfixable failure.

    Click **Set up the environment** to rebuild the venv. As a fallback, **Copy details** copies the exact error, and you can reinstall the SDK by hand into the venv the app resolves — the desktop pins a floor of `praisonaiagents>=1.7.2`:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    cd src/praisonai-agents
    .venv/bin/pip install "praisonaiagents>=1.7.2"
    ```
  </Accordion>

  <Accordion title="First-run setup was interrupted">
    Closing the app mid-setup — or a killed `uv`/`pip` — leaves the venv half-built. On the next launch the startup banner offers two buttons:

    * **Set up the environment** — rebuild the venv in place (fetch `uv`, install a pinned CPython, reinstall `praisonaiagents`).
    * **Copy details** — copy the exact error to paste into an issue.

    Click **Set up the environment** first; use **Copy details** only if the rebuild also fails.
  </Accordion>

  <Accordion title="Library fixes never reached an existing install (pre-4670)">
    On earlier releases the desktop app built its Python venv **once**, during first-run setup, and never revisited it. Setup ran only from the **Get started** button; a normal launch started the engine from an existing venv without checking what was inside. So a fix shipped in `praisonaiagents` after that reached brand-new installs only — raising the floor in one release changed nothing for installs that already existed. A user stuck on `praisonaiagents 1.7.1` kept hitting `"the engine produced no output"` on every tool call while the fix had been on PyPI for days, with no in-app way to correct it. The workaround was deleting the venv by hand.

    Fixed in PraisonAI [#4670](https://github.com/MervinPraison/PraisonAI/pull/4670) (closes [#4668](https://github.com/MervinPraison/PraisonAI/issues/4668)). The app now writes a `.praisonai-packages` stamp inside the venv and, on every launch, compares it against the current requirement — if they differ, it re-runs the install step once, rewrites the stamp on success, and starts the engine against the up-to-date venv. The tray reads `Engine: updating…` while it runs; a no-drift launch is not slowed because `uv` is near-instant when nothing changed. Failure never blocks chat: the engine still starts and the UI reports whatever version is actually installed, and because the stamp is only written on install success, a failed update is retried next launch rather than silently recorded as done. Reconciliation only touches the app's own venv (`<data>/venv`) — a `PRAISONAI_PYTHON` override or a checkout venv is left untouched, its owner manages its packages.

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Old[🕰️ Pre-4670<br/>venv frozen at first run] --> Stuck[🛑 library fix never reached user]
        New[✨ Post-4670<br/>stamp inside venv] --> Check{Stamp matches<br/>requirement?}
        Check -->|yes| Fast[✅ starting engine…]
        Check -->|no| Reapply[⚙️ Engine: updating…]
        Reapply --> Success{Install ok?}
        Success -->|yes| Rewrite[💾 rewrite stamp]
        Success -->|no| Fallback[⚠️ start with what is installed]

        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef in fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef work fill:#F59E0B,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

        class Old,Stuck bad
        class New,Check,Success in
        class Reapply,Rewrite work
        class Fast ok
        class Fallback bad
    ```
  </Accordion>

  <Accordion title="Engine started against the wrong Python (pre-4519, PYTHONHOME/PYTHONPATH exported)">
    A shell that exports `PYTHONHOME`, `PYTHONPATH`, or `PYTHONSTARTUP` — common with conda, distro tooling, or a systemd user session — used to have those variables passed straight through to the engine. That redirected the engine's stdlib or `site-packages` away from the venv the shell had just proved. The engine either failed to start with `ModuleNotFoundError` for something clearly present in `.venv/`, or worse, started against another environment's `praisonaiagents` and behaved inconsistently.

    Fixed in PraisonAI [#4519](https://github.com/MervinPraison/PraisonAI/pull/4519) (closes [#4500](https://github.com/MervinPraison/PraisonAI/issues/4500)). The shell now strips `PYTHONHOME`, `PYTHONPATH`, and `PYTHONSTARTUP`; sets `VIRTUAL_ENV` to the venv root; and prepends the venv's `bin`/`Scripts` to `PATH`. Every other variable passes through unchanged.

    If you saw the engine importing from the wrong environment, upgrade to this release. To point the engine at a local `praisonaiagents` checkout, use `PRAISONAI_AGENTS_SOURCE` — see the [Environment Variables](/docs/features/desktop/environment-variables) page.

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Shell[🖥️ Your shell env] --> Filter{🔧 spawn_env}
        Filter -->|strip| Poison[❌ PYTHONHOME<br/>❌ PYTHONPATH<br/>❌ PYTHONSTARTUP]
        Filter -->|inject| Fresh[✅ VIRTUAL_ENV<br/>✅ PATH prepended]
        Filter -->|pass through| Rest[✅ HOME, keys, PRAISONAI_*]
        Fresh --> Engine[🧠 Engine]
        Rest --> Engine

        classDef in fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
        classDef mid fill:#189AB4,stroke:#7C90A0,color:#fff

        class Shell in
        class Filter mid
        class Poison bad
        class Fresh,Rest,Engine ok
    ```
  </Accordion>

  <Accordion title="Theme was a dead button and toggles disagreed with the engine when a settings write was rejected (pre-4520)">
    Clicking a Settings row when the engine rejected the write left the UI in one of three misleading states:

    * **Theme** did nothing — no `data-theme`, no highlight, no error, no toast. The button read as broken.
    * **Toggles** (`confirm_delete`, `auto_title`, `show_reasoning`, …) moved in memory but not on screen, so the switch and the persisted value disagreed and the next click flipped the wrong one back.
    * **Text size** toasted `Text size 16 px` while every write silently failed, and two rapid `⌘+` presses collapsed to a single step.

    Fixed in PraisonAI [#4520](https://github.com/MervinPraison/PraisonAI/pull/4520) (closes [#4497](https://github.com/MervinPraison/PraisonAI/issues/4497)). `saveCfg` now persists first, checks `r.ok`, surfaces `Could not save that setting.` on failure, and returns a boolean. `apply` gates `applyTheme`/`modelName.textContent`/`renderSettings` on that boolean. `setTextSize` awaits and only toasts the size that actually stuck. Writes are serialised through a `saveQueue` promise chain so a burst of clicks resolves in order.

    Any Settings write against an unreachable or restarting engine now shows a `Could not save that setting.` toast and leaves the row exactly where it was. If you saw a dead Theme button after changing `base_url` or `api_key`, upgrade to this release; the button was never dead, the engine was mid-restart.

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Click[🖱️ Click Theme / toggle] --> Save{saveCfg}
        Save -->|pre-4520: mutate first,<br/>never check r.ok| Ghost[❌ UI applies value<br/>engine never stored it]
        Save -->|post-4520: persist first,<br/>gate on r.ok| Fork{ok?}
        Fork -->|yes| Apply[✅ mutate + applyPrefs]
        Fork -->|no| Toast[🔔 Could not save that setting.]

        classDef in fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef mid fill:#189AB4,stroke:#7C90A0,color:#fff
        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

        class Click in
        class Save,Fork mid
        class Ghost bad
        class Apply,Toast ok
    ```
  </Accordion>

  <Accordion title="A settings save echoed the API key in cleartext (pre-4680)">
    `GET /settings` had always masked the `api_key` as bullets, but `POST /settings` replied with `save_settings()`'s merged dict — which starts from `load_settings()`, and that reads the real secret back out of the OS keychain. So every settings write answered with the credential in cleartext, **including a write that only changed `theme`** and never mentioned the key. Combined with `Access-Control-Allow-Origin: *`, any page open in a browser could read it.

    Fixed in PraisonAI [#4680](https://github.com/MervinPraison/PraisonAI/pull/4680). A single `redacted()` helper is now applied at **both** `GET /settings` and `POST /settings`, so every settings response masks each `SECRET_KEYS` entry as bullets. An empty key still comes back as `""` so the UI can tell "set" from "unset". What is stored is unchanged; only what leaves the process is masked. If you inspected a settings write on an earlier release and saw your key in cleartext, upgrade — and rotate that key.
  </Accordion>

  <Accordion title="Export silently faked a backup when the engine was unreachable (pre-4518)">
    Opening Settings and clicking **Export all** while the engine was down gave no error and no toast. The clipboard still held whatever it had from earlier in the session, so pasting it into a `.json` file could look like a valid backup and be treated as one.

    Fixed in PraisonAI [#4518](https://github.com/MervinPraison/PraisonAI/pull/4518) (closes [#4502](https://github.com/MervinPraison/PraisonAI/issues/4502)). `runAction` now `try/catch`-es its work and surfaces failures via `toast('That did not run: …')`. Export also checks `if(!r.ok) throw` before touching the clipboard, so a failed export can no longer overwrite the clipboard with an incomplete payload.

    Any Settings action against an unreachable engine now shows a toast prefixed `That did not run:`. If you relied on Export as a backup path on an earlier release, re-verify any Export you took while the pill read `engine failed` or `starting engine`.

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Click[👤 Export all] --> Fetch{🔧 fetch ok?}
        Fetch -->|pre-4518 fail| Silent[🛑 stale clipboard<br/>looks like a backup]
        Fetch -->|post-4518 fail| Toast[✅ That did not run: …]
        Fetch -->|ok| Write[✅ clipboard written]

        classDef user fill:#6366F1,stroke:#7C90A0,color:#fff
        classDef mid fill:#189AB4,stroke:#7C90A0,color:#fff
        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

        class Click user
        class Fetch mid
        class Silent bad
        class Toast,Write ok
    ```
  </Accordion>

  <Accordion title="Engine timed out at startup (non-English Windows / Japanese locale)">
    On a CP932 (Japanese) or CP1252 (Western European) system the engine's reader used to die on the first undecodable byte, the port announcement was lost, and startup blamed the 30-second timeout instead of the real cause.

    The shell now exports `PYTHONUTF8=1` and `PYTHONIOENCODING=utf-8` to the engine and reads its output loss-tolerantly, so this is fixed. Setting `PYTHONUTF8=1` yourself is redundant — the shell already does it.
  </Accordion>

  <Accordion title="No tray icon on Linux (Wayland / GNOME)">
    On a Wayland host with no appindicator, tray initialisation can fail — the app still launches. Tray failure is logged rather than fatal, so a missing tray icon is not a crash. On Debian/Ubuntu, install `libayatana-appindicator3-1` (or `libappindicator3-1`) to get the icon back.
  </Accordion>

  <Accordion title="Stop button did nothing on Windows (pre-4382)">
    On Windows a training **Stop** used to report success while the run kept going and held the GPU — the stop signal needed a console the trainer did not have. This is fixed: Stop now uses `taskkill /T /F`. If you observed it, upgrade to this release.
  </Accordion>

  <Accordion title="A fine-tune died on every relaunch on Windows (pre-4515)">
    On Windows the shell compared the interpreter path in the lockfile (`sys.executable`, always backslashed) against the expected path (built with a forward slash), and a byte mismatch escalated to `taskkill /T /F`. That took the engine **and** its training subprocess with it on every relaunch, so a fine-tune could never survive closing the window.

    Fixed in PraisonAI [#4515](https://github.com/MervinPraison/PraisonAI/pull/4515) (closes [#4499](https://github.com/MervinPraison/PraisonAI/issues/4499)). The shell now folds separators and case on Windows before comparing. If you saw a fine-tune disappear every time you reopened the app, upgrade to this release.
  </Accordion>

  <Accordion title="A fine-tune vanished after an engine restart (pre-4510)">
    The engine kept the live run and the history in memory only, so a crash or kill left `/train/runs` returning `[]` while the trainer kept the GPU — and `start` was willing to launch a second trainer beside the live one. Fixed in PraisonAI [#4510](https://github.com/MervinPraison/PraisonAI/pull/4510): the run's state and pid are persisted to `runs/<run-id>/run.json` at every transition, and the next engine boot rebuilds history from disk. A run is adopted only when its recorded pid **and** its start-time fingerprint still match the live process; an interrupted run whose pid is gone reads as `failed`, not missing. If you saw a run disappear across a restart, upgrade.

    Adopting purely on a live pid was itself a hazard — pids are recycled, and adopting a recycled one left the run pinned `running` forever (`start` refused every new fine-tune until someone deleted `run.json` by hand) while Stop SIGTERM'd whatever unrelated program now held that pid. PraisonAI [#4680](https://github.com/MervinPraison/PraisonAI/pull/4680) added the start-time fingerprint (`pid_start`) so adoption proves ownership before reattaching; a pre-upgrade state file with no fingerprint is refused rather than adopted, at the cost of at most one run shown as interrupted across a single restart after upgrade. If fine-tuning was wedged with no live process, this is fixed — you no longer need to delete `run.json` by hand.
  </Accordion>

  <Accordion title="Every training Start refused with 409 until an engine restart (pre-4509)">
    A single `OSError` during log writing abandoned the pipe while the trainer was still writing. The child blocked on a full pipe, `proc.wait()` blocked on the child, `run.finish()` never ran, and the run's state stayed `running` forever — so `POST /train/start` returned `409` for every subsequent run until the engine was restarted.

    Fixed in PraisonAI [#4509](https://github.com/MervinPraison/PraisonAI/pull/4509) (closes [#4493](https://github.com/MervinPraison/PraisonAI/issues/4493)). The reader now always drains the pipe, log errors are caught per-line, and every line is `flush()`ed so `train.log` is the live full record. If you saw a run pinned to `running` and every subsequent Start refused, upgrade.
  </Accordion>

  <Accordion title="Quitting the app orphaned a running fine-tune (pre-4508, macOS/Linux)">
    On **Quit**, the engine exited but the trainer — spawned in its own session — was reparented to `init`, kept the GPU, and became invisible to the app.

    Fixed in PraisonAI [#4508](https://github.com/MervinPraison/PraisonAI/pull/4508) (closes [#4491](https://github.com/MervinPraison/PraisonAI/issues/4491)). The engine's exit handler now calls `Trainer.stop()` before exiting, sending `SIGTERM` to the trainer's own session/group. Windows was already correct (`taskkill /T`) and is unaffected. If you saw the GPU stay pinned after quitting on macOS or Linux, upgrade.
  </Accordion>

  <Accordion title="Point the engine at a local praisonai-agents checkout">
    Set `PRAISONAI_AGENTS_SOURCE` to your checkout before launching:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    export PRAISONAI_AGENTS_SOURCE=/absolute/path/to/praisonai-agents
    ```

    Without the override, the engine walks up from its own file looking for `praisonai-agents/` or `src/praisonai-agents/`. Earlier builds used a fixed path that resolved to a nonexistent directory from the bundled copy, so any local fix appeared to have no effect — that is now fixed.
  </Accordion>

  <Accordion title="The sidebar looks broken after I dropped an exported backup into chats/">
    **Symptom (before this fix):** the `/chats`, `/projects`, and `/search` endpoints dropped the connection with no status, so the front end blamed the engine for being down. Reproduced by placing a JSON **array** (for example, the app's own **Export** clipboard payload) into `chats/`.

    **Fix:** upgrade to the release that includes PraisonAI [#4512](https://github.com/MervinPraison/PraisonAI/pull/4512). The engine now treats any non-object JSON file in `chats/` as `(unreadable)` and keeps serving the other conversations.

    **What to do with the file:** the wrong-shape file appears as an `(unreadable)` row. To load those conversations properly, split the Export array into one JSON object per chat and drop each object into `chats/` as its own file (`<id>.json`).

    ```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    graph LR
        Bad[📄 Wrong-shape file] --> List[🧠 list_chats]
        Bad --> Load[🧠 load_chat]
        List --> Row[✅ (unreadable) row]
        Load --> Row2[✅ (unreadable), not a dropped connection]

        classDef bad fill:#8B0000,stroke:#7C90A0,color:#fff
        classDef engine fill:#189AB4,stroke:#7C90A0,color:#fff
        classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

        class Bad bad
        class List,Load engine
        class Row,Row2 ok
    ```
  </Accordion>

  <Accordion title="engine failed: address in use">
    Another PraisonAI process holds the port (`Address already in use`). Quit the other process, or reset the engine and relaunch.
  </Accordion>

  <Accordion title="engine failed: crashed">
    An unhandled exception reached the top of the stack. The tail is shown — open the **Engine Log** for the full 400-line buffer.
  </Accordion>

  <Accordion title="No virtual environment found">
    The app checks `src/praisonai-agents/.venv`, `src/praisonai-agents/venv`, then `venv`. Create one of these and install `praisonaiagents` into it.
  </Accordion>

  <Accordion title="Launched, but ExitCode=0 and no window (Windows)">
    The single-instance guard exits a secondary launch with code `0` after raising the existing window into the tray, so a bare `Start-Process -PassThru` reads that as a crash. Open `%TEMP%\PraisonAI-startup.log`: a `secondary` line for that pid confirms the launch was handed off cleanly and the primary is still running — check the system tray. If no line was written at all, the shell died before it could even trace itself; attach the missing log path to a bug report.
  </Accordion>

  <Accordion title="&#x22;Open at login&#x22; won't stay on">
    Turning on **Open at login** requires a macOS `.app` bundle so the engine can register a LaunchAgent. When the app runs from a checkout, or on Windows or Linux, the OS refuses the request. As of PraisonAI [#4507](https://github.com/MervinPraison/PraisonAI/pull/4507) the engine persists the honest result (`false`) instead of the request (`true`), the toggle snaps back to off, and the inline row message shows `Only available in the installed app.` If you're on macOS and want to test it from a checkout, set `PRAISONAI_APP_BUNDLE` to your built `.app` path before launching. On an earlier release, upgrading is the fix — pre-4507 the toggle would render on and survive restarts while no login item existed anywhere.
  </Accordion>
</AccordionGroup>

<Note>
  The fallback "No output" banner now suppresses itself when the engine has already reported a specific error kind (`no_answer`, `auth`, `rate_limit`, `internal`) and after **Stop** — one turn produces at most one banner.
</Note>

***

## Updates

The update check asks PyPI what the newest `praisonaiagents` is: `GET /update` hits `https://pypi.org/pypi/praisonaiagents/json` and reports whether a newer release exists.

| Result              | Message                                                  |
| ------------------- | -------------------------------------------------------- |
| Newer release found | `praisonaiagents X is available.`                        |
| Already current     | `You are on the latest version.`                         |
| Check turned off    | `Update checks are off.`                                 |
| PyPI unreachable    | `Could not reach PyPI: …` (reported as `checked: false`) |

The `check_updates` toggle controls whether the app checks on startup and from **About → Check for updates now**. A check that did not happen reports `checked: false` rather than falsely reading as "up to date".

<Note>
  The **PyPI update check** is a version check only — the app does not automatically upgrade to a newer PyPI release. Install a newer `praisonaiagents` by re-running provisioning, or by upgrading your own pinned venv if you set `PRAISONAI_PYTHON`.

  The app **does** automatically reconcile its own venv to the requirement shipped with the current build (see [Library fixes never reached an existing install (pre-4670)](#library-fixes-never-reached-an-existing-install-pre-4670)). So a raised floor in the shipped requirement reaches you on the next launch; a newer PyPI release beyond that floor still requires a manual step.
</Note>

***

## Reset Recipe

<Steps>
  <Step title="Quit the app">
    Fully close the window so the engine process exits.
  </Step>

  <Step title="Delete the lockfile">
    Remove the lockfile in the data directory for your platform:

    | Platform | Lockfile location                                          |
    | -------- | ---------------------------------------------------------- |
    | macOS    | `~/Library/Application Support/PraisonAI`                  |
    | Windows  | `%APPDATA%\PraisonAI`                                      |
    | Linux    | `~/.local/share/PraisonAI` (or `$XDG_DATA_HOME/PraisonAI`) |
  </Step>

  <Step title="Relaunch">
    Reopen the app — the shell spawns a fresh engine and the pill returns to `starting engine`.
  </Step>
</Steps>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Check the log before filing a ticket">
    The engine log is a 400-line ring buffer of recent activity. It usually names the failure directly.
  </Accordion>

  <Accordion title="Keep one PraisonAI process at a time">
    "Address in use" comes from a second process on the port. Close extras before relaunching.
  </Accordion>

  <Accordion title="Match the interpreter to its venv">
    The shell refuses an interpreter whose site-packages live outside its own venv. Use a clean `.venv` inside the checkout to avoid mismatches.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Overview" icon="display" href="/docs/features/desktop/index">
    Install, launch, and how the engine starts
  </Card>

  <Card title="Data & Privacy" icon="lock" href="/docs/features/desktop/data">
    Where the data directory and lockfile live
  </Card>
</CardGroup>
