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

# Troubleshooting

> When a device won't come up — where Omega writes the reason, the common causes, and the fix loop.

Omega is **fatal on a bad configuration by design**: it refuses to run half-configured rather than come up in a state where the platform thinks it can control the device but something silently isn't wired. The upside is a strong guarantee — *a device that is online is running exactly the profile you gave it* — and the trade-off is that most trouble looks the same: **the service starts and immediately exits.** The good news is Omega tells you why.

## Where the reason is written

When startup fails, Omega makes it legible in three places:

* **Exit code `78`** (`EXIT_STARTUP_FAILED`) — a config/startup problem, distinct from a crash. If your service manager shows the exit code, this is the first signal.
* **`startup-error.log`**, written next to the profile, with the exact reason.
* **Windows Event Log** — the same reason, because the Service Control Manager's own "the service did not return an error" message says nothing useful. Look here first on Windows.

The fix loop is always the same:

<Steps>
  <Step title="Read the reason">
    Open `startup-error.log` (or the Windows Event Log entry). It names the specific problem — a module, a capability, a field, or a topic.
  </Step>

  <Step title="Validate the fix at your desk">
    ```bash theme={null}
    omega-agent --validate --client /path/to/config.yaml
    ```

    Exit 0 means the binary can honor the profile. Iterate here, not on the device.
  </Step>

  <Step title="Restart the service">
    `systemctl restart omega` · `launchctl kickstart` · `sc.exe start omega`.
  </Step>
</Steps>

There is no auto-fallback to a previous config — if someone ships a bad profile, the device comes up reporting the problem rather than silently running something else.

## Common causes

### "requires module/capability X, not provided by this build"

The profile lists a module in `modules.required` or a flag in `security.capabilities` whose [Cargo feature](/edge/install) isn't compiled into this binary. The two halves — the build and the profile — have drifted.

**Fix:** either rebuild the SKU with that feature (`cargo build … --features X`) or remove it from the profile. Then `--validate`. Wire `--validate` into CI so a SKU/profile mismatch fails the build, not a device.

### The device connects but every subscribe fails (denied SUBACK)

mTLS succeeds, then the broker denies the subscriptions and Omega tears down. This is **fatal on purpose** — a device won't come up half-controllable. It's an ACL/permissions problem on the broker for that device's topics.

**Fix:** grant the device's identity access to its topic tree (`{root_topic}/#` and the `$golain/devices/{uuid}/cert/*` topics). See the [topic reference](/edge/connect#topic-reference) for the full set the device needs.

<Note>
  A frequent version of this: `connection.root_topic` left **empty** defaults to `omega/{device_id}`, which many brokers deny by ACL. If mTLS connects and every subscribe fails, check that `root_topic` is set to a tree the device is actually allowed on.
</Note>

### `require_hardware` and no hardware key

`OMEGA_KEY_PREFERENCE=require_hardware` on a binary built without a keystore backend for that OS — or one where the secure element holds no key — refuses to start rather than fall back to a software key.

**Fix:** build with the right [keystore feature](/edge/hardware-keys#backends-compile-time-per-os), make sure the service account can reach the secure element, or (if a software key is acceptable) set `prefer_hardware`.

### Malformed profile

A YAML syntax error or a field with the wrong type. `startup-error.log` points at it; `--validate` reproduces it locally.

## A device reverted after an update

If a device came back on an *older* agent version after a [self-update](/edge/ota#self-update-over-a-b-slots), the new version failed its trial boot — it didn't come up healthy within `commit_timeout`, or failed the health check, or crash-looped past 3 boot attempts — and the launcher reverted to the last known-good slot. That's the safety net working. Check the new agent's logs from the trial boot to see why it didn't stay up, fix it, and re-deploy.

## Quick reference

| Symptom                                       | Look at                         | Likely cause                                       |
| --------------------------------------------- | ------------------------------- | -------------------------------------------------- |
| Service starts then exits, code 78            | `startup-error.log` / Event Log | Bad profile (see causes above).                    |
| Connects, then goes offline immediately       | `startup-error.log`             | Denied broker subscription (ACL).                  |
| Windows: "service did not return an error"    | **Event Log**                   | Startup failure — the real reason is logged there. |
| Won't start after enabling `require_hardware` | `startup-error.log`             | No hardware backend/key.                           |
| Came back on an older version                 | agent logs                      | Self-update reverted after a failed trial boot.    |

## See also

* [Configure Omega → validation](/edge/configure#validation) — the validation model in full.
* [Deploy Omega](/edge/deploy#when-a-service-wont-start) — the per-OS service failure surface.
* [Connect](/edge/connect#topic-reference) — the topics a device needs the broker to allow.
