slopus/happy · warning

Claude goal action already in progress

Error message

Claude goal action already in progress

What it means

The goal-action handler enforces one in-flight goal mutation at a time via the pendingClaudeGoalAction guard. If a second goal action arrives while one is still executing, it throws this error instead of queueing or interleaving the operations.

Solutions

  1. Wait for the current goal action to finish before sending another.
  2. Retry the action after a short delay if the UI gave no feedback.
  3. Update the app so in-flight actions show a pending state / disable the button.
  4. Restart the session if pendingClaudeGoalAction appears stuck (a bug worth reporting).

Example fix

// before
rpc('goal-action', { action: 'set', goal: 'x' }); // fired while another in flight
// after
if (!goalActionPending) await rpc('goal-action', { action: 'set', goal: 'x' });
Defensive patterns

Strategy: try-catch

Validate before calling

let goalActionInFlight = false;
async function sendGoalAction(p: unknown) {
  if (goalActionInFlight) throw new Error('A goal action is already running');
  goalActionInFlight = true;
  try { await rpc('goal-action', p); } finally { goalActionInFlight = false; }
}

Try / catch

try {
  await sendGoalAction({ action: 'clear' });
} catch (err) {
  if ((err as Error).message.includes('already in progress')) {
    await sleep(1500);
    return sendGoalAction({ action: 'clear' });
  }
  throw err;
}

Prevention

When it happens

Trigger: A second 'goal-action' RPC arrives while pendingClaudeGoalAction is still set — e.g. the user taps set/clear twice quickly in the mobile app, or a retry from the app duplicates an in-flight request.

Common situations: Double-tap on the goal UI; slow goal application making the first action appear stuck; app-side retry logic re-sending an unacknowledged action.

Related errors


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

Appendix: source

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

        model: currentModel,
        fallbackModel: currentFallbackModel,
        customSystemPrompt: currentCustomSystemPrompt,
        appendSystemPrompt: currentAppendSystemPrompt,
        allowedTools: currentAllowedTools,
        disallowedTools: currentDisallowedTools,
        effort: currentEffort,
    });

    session.rpcHandlerManager.registerHandler('goal-action', async (params: unknown) => {
        const actionParams = params && typeof params === 'object' && !Array.isArray(params)
            ? params as Record<string, unknown>
            : null;
        const command = actionParams ? parseClaudeGoalActionParams(actionParams) : null;
        if (!command) {
            throw new Error('Unsupported Claude goal action');
        }
        if (pendingClaudeGoalAction) {
            throw new Error('Claude goal action already in progress');
        }
        if (!latestClaudeGoalStatus || latestClaudeGoalStatus.status !== 'active') {
            throw new Error('No active Claude goal');
        }

        const capabilities = latestClaudeGoalStatus.capabilities ?? {};
        if (command.type === 'clear' && !capabilities.clear) {
            throw new Error('Claude clear goal action is not supported');
        }
        if (command.type === 'set' && !capabilities.edit) {
            throw new Error('Claude edit goal action is not supported');
        }
        if (currentRunMode !== 'remote') {
            throw new Error('Claude goal action is not ready: remote mode is not active');
        }
        if (!currentSession || currentSession.thinking) {
            throw new Error('Claude goal action is not ready while Claude is thinking');
        }

View on GitHub (pinned to b824cd0a46)