Common Issues

The quickest fixes for the most frequent problems.

Licence gate failures

Symptoms: Boot fails with No carrier licence found at <path>. A valid licence file is required to start the platform. Install a licence at $HOZIRON_HOME/licence.json. — or, at invocation time, No carrier licence loaded — all model invocations require a valid licence. The licence encodes contracted workflow budgets and provider entitlements.

The platform requires a valid carrier licence to start the routing gateway; without one, model invocations are blocked entirely (hoziron-cli licence status reports "status": "no_licence", "enforcement": "all_blocked").

Fix: Provision a real licence file at $HOZIRON_HOME/licence.json (default ~/.hoziron/licence.json). There is exactly one licensing path — no environment variable, test flag, or config option bypasses it. If you're issuing licences yourself (design-partner/internal tooling), see xtask licence issue; otherwise obtain one from your Hoziron account team.

hoziron-cli licence status --json

An expiring/expired licence shows "status": "expiring_soon" or "status": "expired" in licence status — renew before expiry; the platform does not grant a grace period past expires_at.

Auth posture gate refuses to start

Symptoms: A surface (api/mcp/dashboard/registry) refuses to boot with Refusing to start <surface>: auth.mode="disabled" while bound to network-reachable address '<addr>'. Set auth.mode to "local" or "oidc", bind to loopback for dev, or set auth.allow_insecure_no_auth=true to accept the risk (warns on every boot).

The address named in the error is that surface's effective listen address — its own [surfaces.<name>].listen if it's a main route, or its mount target's if it's mounted (e.g. mcp mounted on api is gated against api's bind address). dashboard is gated the same way as api/mcp — it shares the identical [auth]/key-store/OIDC trust model, so leaving it unauthenticated on a network-reachable bind is refused just like the others.

This is a deliberate boot-time safety check (Issue #512/#521): auth.mode defaults to disabled, which is fine for a single-user loopback dev box but must never silently expose a control plane on a network-reachable bind address.

Fix:

  • Preferred: set auth.mode = "local" (API keys) or "oidc" in config.
  • Local dev only: bind to a loopback address (127.0.0.1:..., ::1:..., localhost:..., or unix://...) — the gate only fires on non-loopback binds.
  • Explicit risk acceptance (not recommended outside throwaway environments): auth.allow_insecure_no_auth = true — this still boots with auth disabled and logs a SECURITY: warning on every startup.

Config validation errors on boot

Symptoms: Boot or hoziron-cli config set/unset fails with a ValidationError/InitError naming a specific field, e.g. provider 'anthropic': invalid api_key_env '...', or server.cors.allowed_origins must not be empty when CORS is configured.

PlatformConfig::validate() (the single validation entry point — a previous, lighter validate_for_config_write() used by hoziron-cli config set/unset was collapsed into it) checks the full provider/model inventory, api_key_env naming, and [server.cors]. It does not know about [surfaces.*] (listen/mount/tls/allowed_ips/limits) — those fields, and the topology/auth-posture checks below, belong to bin/hoziron-server's own config struct and only get validated at real server boot, not by hoziron-cli config set/get/unset (a pure CLI edit against PlatformConfig's view of the file).

Fix:

# Show current config
hoziron-cli config show

# Fix a scalar field PlatformConfig knows about
hoziron-cli config set server.cors.allowed_origins '["https://dashboard.company.com"]'

# surfaces.* fields (listen, mount, tls, allowed_ips, limits) aren't
# validated by `hoziron-cli config set` — edit config.toml directly and let
# `hoziron-server` catch topology/hardening mistakes at boot instead
hoziron-cli config edit

The error always names the offending field ("field" detail key) — fix that field and re-run. For a surfaces.* topology or hardening error (e.g. surfaces.mcp: cannot set both listen and mount, or surfaces.api: allowed_ips is set on a unix:// listen address), the message comes from hoziron-server at boot, not from hoziron-cli config set — see TLS and networking for the full validation rules.

Daemon won't start — port in use

# Find what's using port 4200
lsof -i :4200

# Kill it, or use a different port
hoziron-server --api-listen 0.0.0.0:4201

Permission denied in container

The container runs as UID 65532 (distroless nonroot). Ensure the volume is writable:

# Docker
docker run -v hoziron-data:/data ...

# Or with explicit ownership
docker run -v ./local-data:/data --user 65532:65532 ...

If your container platform manages volume ownership itself, set it to group 65532 so the container's UID can write to the mounted volume.

Provider key not working

# Test the key
hoziron-cli config test-key anthropic

# Verify the env var is set
echo $ANTHROPIC_API_KEY

# Re-set it
hoziron-cli config set-key anthropic

Keys are resolved at request time — if you changed the env var, restart the daemon if it was set before the process started.

Agent won't respond to messages

Check the agent's current state — the lifecycle has exactly two states, Suspended and Running:

hoziron-cli agent status <agent-id>

If it's Suspended, activate it:

hoziron-cli agent activate <agent-id>

Activation is gate-checked (ADR-056): it fails if any competency the agent has equipped declares a contract with no installed provider. See agent troubleshooting for the full activation-gate error and fix.

"Cannot apply 'activate' to agent in Running state"

The agent is already running — activate (Suspended → Running) and suspend (Running → Suspended) are the only two lifecycle commands, and each is only valid from one state. Check the agent's current state first:

hoziron-cli agent status <agent-id>

Registry unreachable

hoziron-cli catalog registry test

Check network connectivity and auth tokens.

CORS errors from dashboard

CORS must be configured if the dashboard connects directly:

[server.cors]
allowed_origins = ["https://dashboard.company.com"]
allow_credentials = true

Cannot use wildcard * with allow_credentials = true.

Route panic on startup (axum)

If you see a panic mentioning route syntax or path parameters: ensure path parameters use {param} syntax (axum 0.8), not :param.

Health check shows degraded

hoziron-cli health --json

Check which subsystem is degraded and see the specific troubleshooting page:


Related: