BloopAI/vibe-kanban · warning · Error

Approval has timed out

Error message

Approval has timed out

What it means

The approval feedback flow checks whether the pending approval's timeoutAt deadline has passed before submitting the user's denial reason. If current time exceeds timeoutAt it throws 'Approval has timed out' instead of calling denyAsync. This prevents sending feedback for approvals the executor has already expired/abandoned.

Source

Thrown at packages/web-core/src/features/workspace-chat/model/contexts/ApprovalFeedbackContext.tsx:112

      setNowTs(Date.now());
      reset();
    },
    [reset]
  );

  const exitFeedbackMode = useCallback(() => {
    setActiveApproval(null);
    setNowTs(Date.now());
    reset();
  }, [reset]);

  const submitFeedback = useCallback(
    async (message: string) => {
      if (!activeApproval) return;

      // Check timeout before submitting
      if (new Date() > new Date(activeApproval.timeoutAt)) {
        throw new Error('Approval has timed out');
      }

      await denyAsync({
        approvalId: activeApproval.approvalId,
        executionProcessId: activeApproval.executionProcessId,
        reason: message.trim() || undefined,
      });
      setActiveApproval(null);
    },
    [activeApproval, denyAsync]
  );

  const value = useMemo(
    () => ({
      activeApproval,
      enterFeedbackMode,
      exitFeedbackMode,
      submitFeedback,

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Clear the stale activeApproval state and show a timeout notice instead of letting the throw propagate
  2. Render a live countdown and disable the submit button once timeoutAt is reached
  3. Re-request the approval from the executor if the work is still needed
  4. If it fires unexpectedly early, check client/server clock skew (NTP)

Example fix

// before
await submitFeedback(reason); // throws if timed out
// after
try {
  await submitFeedback(reason);
} catch (e) {
  if (e.message === 'Approval has timed out') {
    clearActiveApproval();
    notify.info('This approval expired. Re-run the task to request a new one.');
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (activeApproval && new Date() > new Date(activeApproval.timeoutAt)) { clearActiveApproval(); return; }

Type guard

function isApprovalLive(a: ActiveApproval | null): boolean { return !!a && new Date() <= new Date(a.timeoutAt); }

Try / catch

try {
  await submitFeedback(reason);
} catch (e) {
  if (e instanceof Error && e.message === 'Approval has timed out') {
    clearActiveApproval();
    notify.info('Approval expired before submission.');
  }
}

Prevention

When it happens

Trigger: User leaves an approval dialog open longer than the approval TTL, then clicks submit/deny — new Date() > activeApproval.timeoutAt. Clock skew between client and server can also trigger it early.

Common situations: User steps away with a modal open (approval TTL typically minutes); long-running tool approval forgotten overnight; system clock changed or VM resumed from sleep shifting the local clock forward.

Understand the failure class

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/8233ab84a55006d2. Report an issue: GitHub.