Stirling-Tools/Stirling-PDF · error · Error
Failed to decline
Error message
Failed to decline
What it means
Thrown by decline() in useParticipantSession after workflowService.declineParticipation (or the following loadSession reload) rejects. Message extraction mirrors submitSignature: axios response.data.message or err.message, falling back to 'Failed to decline'. The error is set into state and re-thrown with the original as cause.
Source
Thrown at frontend/editor/src/proprietary/hooks/workflow/useParticipantSession.ts:98
const decline = useCallback(
async (token: string, reason?: string) => {
setLoading(true);
setError(null);
try {
const updatedParticipant = await workflowService.declineParticipation(
token,
reason,
);
setParticipant(updatedParticipant);
// Reload session
await loadSession(token);
} catch (err: unknown) {
const errorMsg = isAxiosError(err)
? err.response?.data?.message || err.message
: (err instanceof Error ? err.message : undefined) ||
"Failed to decline";
setError(errorMsg);
throw new Error(errorMsg, { cause: err });
} finally {
setLoading(false);
}
},
[loadSession],
);
const downloadDocument = useCallback(
async (token: string) => {
setLoading(true);
setError(null);
try {
const pdfBlob = await workflowService.getParticipantDocument(token);
const url = window.URL.createObjectURL(pdfBlob);
const a = document.createElement("a");
a.href = url;
a.download = session?.documentName || "document.pdf";
document.body.appendChild(a);View on GitHub (pinned to 9ef20dcab8)
Solutions
- Read the axios message from error state to get the real backend reason.
- Confirm the token is still valid before offering decline; refresh the link if expired.
- Catch the re-thrown error at the UI and show the backend message, with a retry only for transient failures.
- Debounce/disable the decline button after first click to avoid races.
Example fix
// before
await decline(token, reason);
// after
try {
await decline(token, reason);
} catch (e) {
showDeclineError(e instanceof Error ? e.message : 'Decline failed');
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!token) {
setError('Token is required to decline');
return;
} Type guard
export function isValidParticipantToken(token: string | undefined): token is string {
return typeof token === 'string' && token.length > 0;
} Try / catch
try {
await decline(token, reason);
} catch (e) {
showDeclineError(e instanceof Error ? e.message : 'Decline failed');
} Prevention
- Debounce the decline button to avoid races from double-clicks.
- Verify the workflow deadline hasn't passed before offering decline.
- Show the hook's error state prominently so the backend's reason reaches the user.
When it happens
Trigger: Backend rejects the decline (token expired, session already completed, participant already declined/signed); network failure during the decline POST; the loadSession reload after a successful decline fails (e.g. session pruned).
Common situations: Participant clicks decline after the workflow deadline passed; admin cancelled the session; the token in the link has expired; double-click on decline races two requests; backend transient 5xx.
Related errors
- Failed to submit signature
- Supabase is not configured
- Unknown policy category: ${id}
- Supabase is not configured. Please use static plans instead.
- Failed to fetch plans: ${error.message}
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/45e742a822a85567.
Report an issue: GitHub.