Update and licensing problems
Bundles that will not upload, releases the updater refuses, host repairs that never reach the host, and licences the appliance cannot verify.
- Version: 0.4
- Role: admin_user
- Type: troubleshooting
Updating and licensing share one root property: both depend on the appliance being able to verify a signature. An appliance with no trust anchor cannot accept a licence and cannot accept a release, and the two failures are usually reported separately by two different people.
For a box that has never been licensed at all, start at Install and activation problems instead.
Find your symptom
| What you see | Runbook |
|---|---|
| The System Updates page shows 0.0.0, or its buttons return 500 | The updates page is wrong or errors |
| The appliance cannot say which release it runs | The updates page is wrong or errors |
| The update check finds nothing, or times out | The channel is unreachable |
| A bundle upload stalls, resumes wrong, or is refused | A bundle will not upload |
| An online install is refused for an unpinned host | A bundle will not upload |
| The updater refuses the release before changing anything | The updater refuses the release |
| The update installed and then rolled itself back | The updater refuses the release |
| A change saved in the dashboard never reached the host | A host repair fails |
| A licence upload is rejected as a bad signature | A licence will not verify |
| The appliance can only ever run its trial | A licence will not verify |
| Every product route answers 402 | The product is locked at 402 |
| One feature is refused with 403 while everything else works | 402, 403 or 423 |
The updates page
The updates page is wrong or errors
A missing version stamp, an unset channel, an unreachable channel and a stalled updater all surface on the same page and look alike. Until the version stamp is restored, every comparison against the channel manifest — and every support answer — is meaningless.
The System Updates page shows 0.0.0, or the update buttons return 500
You might see: System Updates shows version 0.0.0 · current version 0.0.0 · update check returns 500 · install update → 500
Before you start
Checks
- 1
Read the product version the appliance reports
diagnostics · appliance.version = "0.0.0"
- 2
Verify an update channel is configured
diagnostics · appliance.channel
- 3
Verify the release channel answered on the last attempt
diagnostics · appliance.update.channelReachable = true
- 4
Check whether an update or host-repair intent is stuck waiting for the host updater
diagnostics · appliance.update.pendingIntent
- 5
Read the host-repair status, which is what the page renders alongside the version
GET /admin/system-update/host-repair
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | version-file-missing (warning) | The appliance reads its installed version from a file the image build writes. When that file is absent or unreadable the version falls back to 0.0.0, and every comparison against the channel manifest then looks like a downgrade — which is why the page can show 0.0.0 and simultaneously refuse to offer an update. Fix: Run the host repair from Settings → System Updates, which rewrites the appliance version file, then reload the page. If host repair is unavailable, the version can be restored by re-running the installer's finalisation step; see Update the appliance. Verify: diagnostics · appliance.version = "0.0.0" If that does not apply: Attach the support bundle. A 0.0.0 that survives host repair means the image was built without its version stamp. |
| check 2 fails | manifest-url-unset (warning) | No release channel is configured, so the update check has no manifest to fetch. The API rejects the call with a 400 naming the missing manifest URL, which the page surfaces as a failed button rather than as a configuration prompt. Fix: Set the update channel under Settings → System Updates. On an air-gapped appliance, choose the offline channel and upload the update bundle instead of pointing at a URL. Verify: diagnostics · appliance.channel |
| check 3 fails | channel-unreachable (warning) | The channel is configured but the manifest fetch failed — no DNS, no egress, or an outbound proxy that is required and not configured. The fetch failure surfaces as a 500 from the update endpoint. Fix: Fix outbound access to the channel host, or switch the appliance to offline updates. Read the DNS and egress probes in the diagnostics snapshot separately: resolution succeeding does not mean the HTTPS fetch succeeds, and a proxy-only network needs the proxy configured explicitly. Verify: diagnostics · appliance.update.channelReachable = true |
| check 4 fails | updater-daemon-not-running (critical) | An intent is queued but nothing is executing it. The gateway only records the intent; the privileged updater daemon on the host performs the work. When that daemon is not running, intents accumulate and every subsequent action appears to hang or fail. Fix: Start the updater daemon on the appliance host and confirm the queued intent moves out of the queued state. Do not queue further intents until it does; a backlog makes the failure harder to read. Verify: diagnostics · appliance.update.pendingIntent If that does not apply: Attach the support bundle plus the updater daemon's own log from the host. |
| check 5 fails | host-agent-absent (warning) | The host-repair endpoint cannot answer because the host agent has never written its inventory. The page then has no host-side data to render beside the version, and the combination reads as a broken page. Fix: Run the host agent's inventory job on the appliance and reload the page. This is expected on a non-appliance deployment, where there is no host agent at all and the update page is not the supported upgrade path. Verify: GET /admin/system-update/host-repair |
The appliance cannot say which release it is running
You might see: This box cannot say which release it is running · System Updates shows version 0.0.0 · current version 0.1.0 on a box that is not 0.1.0 · the update page offers nothing and shows no version
Before you start
Checks
- 1
Read the product version the appliance reports
diagnostics · appliance.version = "0.1.0"
- 2
Read the host-repair status, which is what rewrites the version stamp
GET /admin/system-update/host-repair
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | no-version-stamp (warning) | The appliance reads its installed version from files the image build and the updater write. When none of them can be read the version falls back to a placeholder, and every comparison against the channel manifest is then meaningless — which is why the page can show a version that is obviously wrong and simultaneously refuse to offer an update. Support answers are guesses until this is fixed. Fix: Run the host repair from Settings → System Updates, which rewrites the appliance version file, then reload the page. A placeholder that survives host repair means the image was built without its version stamp. Verify: diagnostics · appliance.version = "0.1.0" If that does not apply: Attach the support bundle and the version named in the installer's boot menu entry. |
| check 2 fails | host-agent-absent (warning) | The host-repair endpoint cannot answer because the host agent has never written its inventory, so the update page has no host-side data to render and nothing can rewrite the version file. On a non-appliance deployment there is no host agent at all and this page is not the supported upgrade path. Fix: Run the host agent's job on the appliance and reload the page. If this is a compose deployment rather than an appliance, update it by pulling the release images instead. Verify: GET /admin/system-update/host-repair |
The channel is unreachable
Expected on a deliberately air-gapped appliance, which updates from a signed offline bundle. On a connected one it is an egress problem wearing an update-shaped mask.
The update channel is not reachable from the appliance
You might see: The update channel host is not reachable from inside the gateway · Check for updates does nothing · update check returns 500 · the appliance never finds a new version
Before you start
Checks
- 1
Verify an update channel is configured at all
diagnostics · appliance.channel
- 2
Read whether the channel answered on the last attempt
diagnostics · appliance.update.channelReachable = true
- 3
Read the name-resolution results for the hosts the appliance must reach
diagnostics · appliance.dns
- 4
Read whether an outbound proxy is configured, on a network that requires one
diagnostics · appliance.proxy.configured = true
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | channel-unset (warning) | No release channel is configured, so the update check has no manifest to fetch. The API rejects the call, and the page shows a button that fails rather than a prompt to configure anything. Fix: Set the update channel under Settings → System Updates. On an air-gapped appliance, choose the offline channel and upload a signed bundle instead of pointing at a URL. Verify: diagnostics · appliance.channel |
| check 2 fails | channel-blocked (warning) | The channel is configured and the fetch failed. On an appliance that is deliberately air-gapped this is the expected state and needs no action; on a connected one it means the HTTPS path to the channel host is blocked, intercepted, or requires a proxy the appliance does not have. Fix: Read the egress results in the diagnostics snapshot and fix the test that failed rather than the one you assume failed. See The appliance cannot get out. If the appliance is meant to stay offline, switch it to the offline channel so the failure stops being reported as a fault. Verify: diagnostics · appliance.update.channelReachable = true |
| check 3 fails | dns-cannot-resolve-channel (warning) | The appliance cannot resolve the channel host, so the fetch never reaches the network. DNS succeeding elsewhere proves nothing: the appliance uses this network's resolver, not the browser's. Fix: Set a resolver that answers for the channel host and re-run the check. See Network prerequisites. Verify: diagnostics · appliance.dns |
| check 4 fails | proxy-missing (warning) | This site sends everything through a proxy and the appliance has none configured, so the fetch is attempted directly and times out. A timeout looks like a dead channel rather than a missing setting. Fix: Configure the outbound proxy on the appliance and restart the gateway container so it re-reads the setting. Verify: diagnostics · appliance.proxy.configured = true |
Installing
A bundle will not upload
Uploads are resumable, so the failures are about offsets and expiry rather than about the
release. An online install is refused outright unless SYSTEM_UPDATE_ALLOWED_HOSTS pins the
host — empty is the shipped default and used to mean "any https host", so the guard was off
wherever nobody had configured it.
An update bundle will not upload or install from the dashboard
You might see: unknown or expired upload · offset mismatch · incomplete upload: have N of M bytes · bundle filename must end with .cidupd
Before you start
Checks
- 1
Read the bundle's file name
expected · The file ends in `.cidupd`.
- 2
For a resumed upload, check that the server still holds the partial file
expected · The status call returns an offset.
- 3
For an offset mismatch, compare what the client thinks it sent with what the server holds
expected · The client resumes from the offset the server reports.
- 4
For "incomplete upload", compare the assembled size with the declared size
expected · The two sizes agree.
- 5
For an online install, read the update host allowlist
expected · The allowlist names the host in the manifest URL, over https.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | wrong-extension (info) | The file name is normalised to a safe character set and must end in .cidupd. This is the first gate and it fires before anything is read, which is why a corrupted bundle and a renamed one look the same.Fix: Restore the original file name and upload again. Verify: expected · The upload starts. |
| check 2 fails | upload-expired (info) | A resumable upload is a partial file in the incoming directory plus a small metadata file. Once either is cleaned up there is nothing to resume, and the server reports it as unknown rather than starting a silent second copy. Fix: Start the upload again from the beginning. Verify: expected · It reports a growing offset. |
| check 3 fails | offset-drifted (info) | The server refuses an append whose offset does not match the bytes it holds, so a client that retried a chunk cannot duplicate or skip one. The refusal carries the real offset. Fix: Re-query the upload status and resume from the offset the server reports. Verify: expected · The offset advances to the full size. |
| check 4 fails | transfer-truncated (warning) | A short bundle would fail signature verification later, on the host, in the middle of a maintenance window. Refusing at completion keeps that failure in the browser. Fix: Upload again over a stable path. A proxy that buffers or truncates large bodies is the usual cause; upload from a machine on the same network as the appliance to rule it out. Verify: expected · It reports the full size and hands back a bundle path. |
| check 5 fails | online-host-not-allowed (warning) | An online install fetches a manifest over the network, so an unpinned host would be an SSRF primitive with administrator privileges. An empty allowlist used to mean "any https host" and empty is the shipped default, so the guard was off wherever nobody configured it — it now refuses instead. Fix: Set SYSTEM_UPDATE_ALLOWED_HOSTS to the update host and restart the gateway, or install from an uploaded bundle instead. See Updates.Verify: expected · It is queued. If that does not apply: Attach the support bundle and the request id from the failed intent. Do not attach the bundle itself. |
The updater refuses the release
Every refusal before the smoke test happens with nothing changed: signature and manifest verification, the minimum-version check, the file checksums, the database pre-flight probes, and the check that the current release's images still exist so rollback remains possible.
Note
docker image prune on the appliance host removes the images rollback needs. The updater then
refuses to go forward rather than taking a one-way trip — take a hypervisor snapshot before
retrying.
The updater refuses the release, or stops part-way through installing it
You might see: signature missing · bundle not found · compose file missing from release · cannot jump from X to Y: this release requires at least
Before you start
Checks
- 1
Read whether the failure is the release's signature or the appliance's trust anchor
diagnostics · appliance.license.trustAnchorPresent = true
- 2
Read the version this appliance runs against the release's minimum
diagnostics · appliance.version
- 3
Check that the bundle carries everything its manifest references
expected · No file is reported missing or mismatched.
- 4
Check that the images of the release currently installed are still present
expected · Every current-release image inspects successfully.
- 5
Read the database pre-flight probes
expected · Every probe reports clean.
- 6
For an update that installed and then rolled back, read the smoke result
expected · The smoke test passed.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | no-trust-anchor (critical) | Every release is verified against the public keys baked into the appliance. With no anchor present nothing verifies, and the same appliance will also refuse a licence for the same reason. Fix: Follow The appliance has no licence trust anchor. Verify: diagnostics · appliance.license.trustAnchorPresent = true |
| check 2 fails | version-unknown (critical) | The updater compares the running version against the release's declared minimum. An appliance that cannot say what it runs cannot be told whether the jump is legal, and every comparison against the manifest is meaningless. Fix: Follow The appliance cannot say which release it runs. Verify: diagnostics · appliance.version |
| check 3 fails | release-incomplete (warning) | A release is a manifest plus the files it names. Extraction refuses members whose paths escape the work directory, and verification refuses a manifest whose files are absent or whose checksums differ — before anything on the appliance is touched. Fix: Obtain the bundle again and re-upload it. A bundle that fails the same way twice is a build problem, not a transfer problem — report it with the release version. Verify: expected · Verification passes. |
| check 4 fails | current-images-pruned (critical) | Rollback works by re-pinning the images of the release you are on. Once those images have been pruned there is no way back, so the updater refuses to go forward rather than taking a one-way trip. Pinned images carry a keep-tag for exactly this reason. Fix: Restore or re-pull the current release's images, then retry. Take a snapshot of the appliance before retrying, so a rollback exists at the hypervisor level even if it does not exist at the image level. Verify: expected · The pre-install check passes. |
| check 5 fails | preflight-refusal (warning) | The probes exist to stop a migration failing half-way through on data it cannot handle. Nothing has changed at the point the refusal fires, so the appliance is still fully serviceable. Fix: Apply the fix the probe names — it is written into the refusal — then queue the install again. Do not force past a probe. Verify: expected · The probes report clean. |
| check 6 fails | smoke-failed (critical) | The post-update smoke test is the last gate. Its failure means the new release came up and did not work, which is exactly the case rollback exists for. The tail of the failing command is carried in the message. Fix: Let the updater roll back, then read the smoke output and the container logs for the service it exercised. A pgdata snapshot is taken before the migration; restore refuses if that snapshot is missing, so check that the state directory is writable. Verify: expected · The appliance is back on the previous release and healthy. If that does not apply: Attach the support bundle, the updater journal for the request id, and the release version you were installing. |
A host repair fails
Several changes made in the dashboard — a certificate, a TLS mode — are applied by a host-side agent, and the failure mode that costs the most is the quiet one: the write fails, the handler logs and continues, and the dashboard reports success.
A host-side repair or reconfigure step fails
You might see: host command failed · host write failed · cannot determine own image · a certificate was applied in the dashboard and the host never changed
Before you start
Checks
- 1
Check whether the agent can identify its own image
expected · The agent resolves its own image, or `CID_HOST_EXEC_IMAGE` pins one.
- 2
For "host write failed", read which path it was writing
expected · The path is writable and the filesystem has room.
- 3
Read free disk on the host
diagnostics · host
- 4
Check the shared state directory, which several features fail through together
expected · The directory exists and the gateway can write it.
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | cannot-resolve-own-image (critical) | Host operations run in a privileged helper container built from the agent's own image, so that what executes on the host is exactly the code the signed release shipped. When the agent cannot inspect its own container it refuses rather than falling back to something unpinned. Fix: Confirm the agent's container can reach the Docker socket, or pin the helper image explicitly with CID_HOST_EXEC_IMAGE. Restart the agent afterwards.Verify: expected · It completes. |
| check 2 fails | host-path-not-writable (warning) | The agent writes host files through a chroot into the host root. A path that is read-only, immutable, or on a filesystem with no space produces the same "host write failed" with a different tail — read the tail. Fix: Make the path writable, or free space, then trigger the operation again. Verify: expected · It reports success. |
| check 3 fails | disk-full (critical) | A full root filesystem breaks host writes, the state directory and any update that needs room for a second copy of every image — usually all at once, which makes the first symptom you notice arbitrary. Fix: Follow A filesystem is nearly full. Verify: expected · The filesystem has headroom. |
| check 4 fails | state-dir-not-writable (critical) | Several features write intents into one shared directory and log-and-continue when the write fails. The visible symptom is therefore a change that appears to succeed in the dashboard and never reaches the host. Fix: Follow Settings the appliance saves do not survive a restart. Verify: expected · The status reports the change applied. If that does not apply: Attach the support bundle. It carries the host agent's own status documents, which is where the failing command and its output are recorded. |
Licensing
A licence will not verify
The licence file is usually fine. An appliance with no signing public key cannot check it, so every licence looks forged — and with enforcement off the same appliance quietly runs its trial, so the gap only surfaces when the trial ends.
License upload fails with 400 "rejected: signature"
You might see: 400 rejected: signature · 400 rejected: ENOENT · License upload → 400 · licence file will not upload
Before you start
Checks
- 1
Verify the licence trust anchor exists on the host
diagnostics · appliance.license.trustAnchorPresent = true
- 2
Verify the appliance reports an installation id
diagnostics · appliance.license.installationId
- 3
Verify the host clock is disciplined by NTP
diagnostics · host.timeSynced = true
- 4
Read the licence state the gateway reports
GET /admin/license/status → {"state":"active|trial"}
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | missing-anchor (critical) | The licence-signing public key (/etc/cid/license-pubkey.pem) was never baked into this image, or was removed. Verification has nothing to check the signature against, so every licence — including a perfectly valid one — is rejected as forged. The API surfaces this as a 400 with a signature or ENOENT message rather than as a missing-key error, which is why the licence file gets blamed. Fix: Restore the trust anchor on the host and restart the gateway container, then upload the licence again. On an appliance the anchor is part of the image: an image that lacks it was built wrong and the fix is to re-run the host repair, not to hand-place a key of unknown provenance. Follow Licensing and activation; if the appliance was built from an image that shipped without the anchor, collect the support bundle and escalate rather than sourcing a key yourself. Verify: diagnostics · appliance.license.trustAnchorPresent = true If that does not apply: Attach the support bundle. The image build, not the appliance, is at fault. |
| check 2 fails | null-installation-id (critical) | The appliance reports no installation id, so no licence can have been issued for it and no licence can bind to it. This normally means the identity was never generated during first boot, or the file holding it is not readable by the gateway container. Fix: Re-run the first-boot identity step and confirm the id appears on Settings → License. Request a licence only once a stable id is shown; a licence issued against a null id is unusable. See Licensing and activation. Verify: diagnostics · appliance.license.installationId |
| check 3 fails | clock-skew (warning) | Licence validity is bounded by dates. A host clock that is wrong by more than the licence's grace window makes a valid licence appear not-yet-valid or expired, and the error surfaces at the same place as a signature failure. Fix: Correct the host clock and enable NTP or chrony, then restart the gateway container and upload the licence again. See Network prerequisites for the NTP egress the appliance needs. Verify: diagnostics · host.timeSynced = true |
| check 4 fails | wrong-licence-for-this-box (critical) | The trust anchor, installation id and clock are all correct, so the signature check is meaningful — and it fails. The licence was issued for a different installation id, was modified in transit (a mail client re-wrapping lines is the usual culprit), or was truncated. Fix: Re-download the licence file from the issuing portal, transfer it as a binary attachment or over a checksum-verified copy, and upload it again without opening it in an editor. If the installation id in the licence does not match this appliance, request a re-issue. Verify: GET /admin/license/status → {"state":"active|trial"} If that does not apply: Attach the support bundle and the installation id shown on Settings → License. |
This appliance has no licence trust anchor and can only run the trial
You might see: No license trust anchor (/etc/cid/license-pubkey.pem) · no licence can be verified on this box · the appliance only ever runs the built-in trial · every licence file is rejected, whichever one we upload
Before you start
Checks
- 1
Read whether the licence-signing public key exists on the host
diagnostics · appliance.license.trustAnchorPresent = true
- 2
Read whether licence enforcement is switched on, which decides how bad the missing anchor is
diagnostics · appliance.license.requireLicense = false
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 fails | anchor-absent (warning) | The licence-signing public key is part of the appliance image and lives at /etc/cid/license-pubkey.pem. Verification has nothing to check a signature against without it, so every licence — including a valid one — is rejected as forged. With enforcement off the appliance still runs its built-in trial, which is why the missing anchor can go unnoticed until the trial ends. Fix: Run the host repair from Settings → System Updates, which re-establishes the files the image should carry, then re-check the snapshot. An image that still lacks the anchor after repair was built wrong: collect the support bundle and escalate rather than placing a key of unknown provenance by hand. Verify: diagnostics · appliance.license.trustAnchorPresent = true If that does not apply: Attach the support bundle. The image build, not the appliance, is at fault. |
| check 2 fails | enforcement-on-without-anchor (critical) | Enforcement demands a valid licence and the appliance cannot verify one, so every licensed route answers 402 no matter what is uploaded. The appliance is enforcing a check it is structurally unable to pass. Fix: Restore the anchor as above and upload the licence issued for this installation id. See License upload fails with 400 signature. Turning enforcement off is a lab measure only — it disables the entitlement checks the tier relies on. Verify: GET /admin/license/status → {"state":"active|trial"} |
The product is locked at 402
Enforcement is on and no licence resolves as valid. Administration stays reachable so this can be fixed without a rescue procedure. Check the host clock before requesting a renewal — a drifting clock expires a valid licence.
Every request returns 402 LICENSE_EXPIRED after the wizard completes
You might see: 402 LICENSE_EXPIRED · chat returns 402 after finishing setup · the dashboard loads but every action fails with payment required · product blocked immediately after setup
Before you start
Checks
- 1
Read whether licence enforcement is switched on for this deployment
diagnostics · appliance.license.requireLicense = true
- 2
Read the licence state the gateway resolved at boot
diagnostics · appliance.license.state = "active|trial"
- 3
Verify the licence trust anchor exists, since without it no licence can ever resolve as active
diagnostics · appliance.license.trustAnchorPresent = true
- 4
Check how long the installed licence has left
diagnostics · appliance.license.expiresAt
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 3 fails | enforcement-on-without-anchor (critical) | CID_REQUIRE_LICENSE is true and the licence-signing public key is absent. The guard demands a valid licence, and verification cannot succeed without the anchor, so every licensed route answers 402 no matter what is uploaded. The appliance is enforcing a check it cannot pass. Fix: Restore the trust anchor and upload the licence, following License upload fails with 400 signature. Turning enforcement off is a deliberate, temporary measure for a lab only — never the fix on a customer appliance, because it disables the entitlement checks the tier relies on. Verify: diagnostics · appliance.license.trustAnchorPresent = true |
| check 2 fails | no-valid-license (critical) | Enforcement is on and the licence resolved to a state other than active or trial — missing, invalid or expired. The setup wizard does not install a licence, so a freshly completed appliance with enforcement on reaches exactly this state until one is uploaded. Fix: Upload the licence issued for this appliance under Settings → License. If no licence has been issued yet, request one against the installation id shown on that page. See Licensing and activation. Verify: GET /admin/license/status → {"state":"active|trial"} |
| check 4 fails | license-genuinely-expired (critical) | A licence is installed and verifies, but its validity period has ended. Enforcement then blocks the product routes while leaving administration reachable, so the appliance can be re-licensed without a rescue procedure. Fix: Upload a renewed licence. If the expiry looks wrong, check the host clock first — a skewed clock ages a valid licence out early and produces exactly this state. Verify: diagnostics · appliance.license.state = "active|trial" |
| check 1 fails | unexpected-402-without-enforcement (warning) | Enforcement is off, so the licence guard should not be blocking anything. A 402 in this state means the value the gateway read at boot differs from the value now configured — usually the container was started before the setting changed and has not been restarted. Fix: Restart the gateway container so it re-reads its configuration, then retry. If 402 persists with enforcement off, collect the support bundle: the effective configuration and the configured one disagree. Verify: diagnostics · appliance.license.requireLicense = false If that does not apply: Attach the support bundle, including the effective posture section. |
The licence has expired and licensed endpoints answer 402
You might see: License expired / product locked · 402 LICENSE_EXPIRED · License state is "expired" · the trial ran out
Before you start
Checks
- 1
Read the licence state the gateway resolved
diagnostics · appliance.license.state = "active|trial"
- 2
Verify the host clock is disciplined, because a skewed clock ages a valid licence out early
diagnostics · host.timeSynced = true
- 3
Verify the trust anchor exists, since without it no renewal can ever verify either
diagnostics · appliance.license.trustAnchorPresent = true
- 4
Read the licence status the API reports
GET /admin/license/status → {"state":"active|trial"}
Causes and fixes
| Reached when | Cause | Explanation, fix and verification |
|---|---|---|
| check 1 or 4 fails | expired-or-invalid (critical) | A licence is installed and its validity period has ended, or it no longer verifies. The guard then blocks the licensed routes — chat, detection, inspection, image and document analysis — with 402 while leaving administration reachable, so the appliance can be re-licensed without a rescue procedure. Fix: Upload a renewed licence under Settings → License. Request the renewal against the installation id shown on that page: a licence issued for a different id will not verify here. See Licensing and activation. Verify: diagnostics · appliance.license.state = "active|trial" |
| check 2 fails | clock-skew-fake-expiry (warning) | Licence validity is bounded by dates read from the host clock. A clock that is wrong by more than the grace window makes a current licence look expired, and the product blocks exactly as it would for a real expiry. Fix: Correct the host clock and enable time synchronisation, then restart the gateway container and re-read the licence state before requesting a renewal. See The host clock is not synchronised. Verify: diagnostics · host.timeSynced = true |
| check 3 fails | renewal-cannot-verify (critical) | The trust anchor is absent, so the renewal you are about to upload will be rejected for the same reason the current licence no longer verifies. Uploading first wastes the maintenance window. Fix: Restore the trust anchor before uploading anything. See This appliance has no licence trust anchor. Verify: diagnostics · appliance.license.trustAnchorPresent = true |
Related
- Updates — the update channel, offline bundles and what an update touches.
- System updates — the dashboard page.
- Licensing — issuing, installing and renewing a licence.
- Error codes — what 402, 403 and 423 mean precisely.
Last updated on