slopus/happy · error

Daemon-spawned sessions cannot use local/interactive mode. U

Error message

Daemon-spawned sessions cannot use local/interactive mode. Use --happy-starting-mode remote or spawn sessions directly from terminal.

What it means

runClaude validates the spawn configuration before creating a session: sessions started by the Happy daemon run without a TTY, so local/interactive mode (which requires an attached terminal) is invalid. This fail-fast check throws when startedBy='daemon' and startingMode='local'.

Source

Thrown at packages/happy-cli/src/claude/runClaude.ts:89

    resolve: (value: { ok: true }) => void;
    reject: (error: Error) => void;
    timeout: ReturnType<typeof setTimeout>;
};

export async function runClaude(credentials: Credentials, options: StartOptions = {}): Promise<void> {
    logger.debug(`[CLAUDE] ===== CLAUDE MODE STARTING =====`);
    logger.debug(`[CLAUDE] This is the Claude agent, NOT Gemini`);
    
    const workingDirectory = process.cwd();
    const sessionTag = randomUUID();

    // Log environment info at startup
    logger.debugLargeJson('[START] Happy process started', getEnvironmentInfo());
    logger.debug(`[START] Options: startedBy=${options.startedBy}, startingMode=${options.startingMode}`);

    // Validate daemon spawn requirements - fail fast on invalid config
    if (options.startedBy === 'daemon' && options.startingMode === 'local') {
        throw new Error('Daemon-spawned sessions cannot use local/interactive mode. Use --happy-starting-mode remote or spawn sessions directly from terminal.');
    }

    // Set backend for offline warnings (before any API calls)
    connectionState.setBackend('Claude');

    // Create session service
    const api = await ApiClient.create(credentials);

    // Create a new session
    let state: AgentState = {};

    // Get machine ID from settings (should already be set up)
    const settings = await readSettings();
    let machineId = settings?.machineId
    const sandboxConfig = options.noSandbox ? undefined : settings?.sandboxConfig;
    const sandboxEnabled = Boolean(sandboxConfig?.enabled);
    const initialPermissionMode = applySandboxPermissionPolicy(
        resolveInitialClaudePermissionMode(options.permissionMode, options.claudeArgs),

View on GitHub (pinned to b824cd0a46)

Solutions

  1. Start the daemon-spawned session with --happy-starting-mode remote.
  2. Remove any local-mode default from the daemon session config.
  3. Spawn the session directly from your terminal (plain `happy claude`) if you need interactive mode.
  4. Check daemon logs/config for a stale startingMode setting.

Example fix

// before
happy daemon start --happy-starting-mode local
// after
happy daemon start --happy-starting-mode remote
Defensive patterns

Strategy: validation

Validate before calling

const isDaemonSpawn = process.env.HAPPY_STARTED_BY === 'daemon';
const mode = process.env.HAPPY_STARTING_MODE ?? 'local';
if (isDaemonSpawn && mode === 'local') throw new Error('Daemon sessions must use --happy-starting-mode remote');

Type guard

function isValidDaemonConfig(o: { startedBy: string; startingMode: string }): boolean {
  return !(o.startedBy === 'daemon' && o.startingMode === 'local');
}

Try / catch

try {
  await runClaude(options);
} catch (err) {
  if ((err as Error).message.includes('Daemon-spawned sessions')) {
    console.error('Daemon + local mode is unsupported. Rerun with: happy --happy-starting-mode remote');
    process.exit(1);
  }
  throw err;
}

Prevention

When it happens

Trigger: A session is spawned via `happy daemon ...`/daemon scheduling while options.startingMode resolves to 'local' — e.g. daemon config requests local mode, or CLI flags/env set --happy-starting-mode local for a daemon-spawned session.

Common situations: Configuring the daemon to auto-start sessions that default to local mode; passing --happy-starting-mode local in a daemon wrapper script; upgrading the daemon and reusing an old config that predates this restriction.

Related errors


AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31). Data as JSON: /api/errors/36d8f0a9e777ab13. Report an issue: GitHub.