iOfficeAI/AionUi · critical

[WebUI] Cannot start: aioncore is not running (globalThis.__

Error message

[WebUI] Cannot start: aioncore is not running (globalThis.__backendPort unset)

What it means

Thrown by the WebUI seeding path when globalThis.__backendPort is unset, i.e. the aioncore backend process has not been started (or its port was never recorded) before the WebUI initialization ran.

Source

Thrown at packages/desktop/src/process/bridge/webuiBridge.ts:55

    const json = (await res.json()) as { data?: AdminUsernameResult | null };
    return json.data?.username ?? 'admin';
  } catch {
    return 'admin';
  }
}

/**
 * On first Enable-WebUI click after a fresh install, the backend's users table
 * holds the seeded `system_default_user` row with an empty password_hash.
 * Probe /api/auth/status; if `needs_setup === true`, ask backend to generate
 * and persist a random password, then stash the plaintext for Settings to show
 * once. When the backend already has credentials (upgrade path handled by
 * ensureAdminUser, or a prior Enable-WebUI), this is a no-op.
 */
async function maybeSeedInitialPassword(): Promise<void> {
  const port = getBackendPort();
  if (!port) {
    throw new Error('[WebUI] Cannot start: aioncore is not running (globalThis.__backendPort unset)');
  }
  const statusRes = await fetch(`http://127.0.0.1:${port}/api/auth/status`);
  if (!statusRes.ok) {
    throw new Error(`[WebUI] /api/auth/status returned ${statusRes.status}`);
  }
  const statusJson = (await statusRes.json()) as { needs_setup?: boolean; data?: { needs_setup?: boolean } };
  const needsSetup = statusJson.needs_setup ?? statusJson.data?.needs_setup ?? false;
  if (!needsSetup) {
    setDesktopWebUIInitialPassword(undefined);
    return;
  }
  const resetRes = await fetch(`http://127.0.0.1:${port}/api/webui/reset-password`, { method: 'POST' });
  if (!resetRes.ok) {
    throw new Error(`[WebUI] /api/webui/reset-password returned ${resetRes.status}`);
  }
  const resetJson = (await resetRes.json()) as { data?: { new_password?: string }; new_password?: string };
  const newPassword = resetJson.data?.new_password ?? resetJson.new_password;
  if (!newPassword) {

View on GitHub (pinned to 711aa0550e)

Solutions

  1. Ensure backendManager.start() has resolved and set __backendPort before initWebuiBridge runs
  2. Check the backend startup logs for a crash or early exit that prevented port assignment
  3. If the backend intentionally isn't running, skip WebUI seeding instead of letting it throw
  4. Make the error message actionable by including backend startup state in logs

Example fix

// before
const port = getBackendPort();
if (!port) {
  throw new Error('[WebUI] Cannot start: aioncore is not running (globalThis.__backendPort unset)');
}

// after (await backend readiness instead of failing)
await waitForBackendPort(10_000);
const port = getBackendPort();
if (!port) {
  throw new Error('[WebUI] Cannot start: aioncore is not running (globalThis.__backendPort unset)');
}
Defensive patterns

Strategy: validation

Validate before calling

const port = (globalThis as { __backendPort?: number }).__backendPort;
if (!port) { /* defer WebUI init until backendManager reports ready */ }

Type guard

function isBackendRunning(): boolean {
  return typeof (globalThis as { __backendPort?: number }).__backendPort === 'number';
}

Try / catch

catch (err) { log.error('[WebUI] init deferred:', err.message); /* schedule retry, don't crash app */ }

Prevention

When it happens

Trigger: Calling maybeSeedInitialPassword (via initWebuiBridge) before backendManager.start() completed and set globalThis.__backendPort, or after the backend failed to start.

Common situations: Race between backend startup and WebUI bridge init, backend crash during startup, or running the WebUI bridge in a context where the backend was intentionally not spawned.

Related errors


AI-assisted analysis of iOfficeAI/AionUi@711aa0550e (2026-08-28). Data as JSON: /api/errors/9d893582f2632c10. Report an issue: GitHub.