Skip to main content
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:
1

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.
2

Validate the fix at your desk

Exit 0 means the binary can honor the profile. Iterate here, not on the device.
3

Restart the service

systemctl restart omega · launchctl kickstart · sc.exe start omega.
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 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 for the full set the device needs.
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.

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, 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, 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

See also