{"record":{"id":"45e742a822a85567","repo":"Stirling-Tools/Stirling-PDF","slug":"failed-to-decline","errorCode":null,"errorMessage":"Failed to decline","messagePattern":"Failed to decline","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/hooks/workflow/useParticipantSession.ts","lineNumber":98,"sourceCode":"  const decline = useCallback(\n    async (token: string, reason?: string) => {\n      setLoading(true);\n      setError(null);\n      try {\n        const updatedParticipant = await workflowService.declineParticipation(\n          token,\n          reason,\n        );\n        setParticipant(updatedParticipant);\n        // Reload session\n        await loadSession(token);\n      } catch (err: unknown) {\n        const errorMsg = isAxiosError(err)\n          ? err.response?.data?.message || err.message\n          : (err instanceof Error ? err.message : undefined) ||\n            \"Failed to decline\";\n        setError(errorMsg);\n        throw new Error(errorMsg, { cause: err });\n      } finally {\n        setLoading(false);\n      }\n    },\n    [loadSession],\n  );\n\n  const downloadDocument = useCallback(\n    async (token: string) => {\n      setLoading(true);\n      setError(null);\n      try {\n        const pdfBlob = await workflowService.getParticipantDocument(token);\n        const url = window.URL.createObjectURL(pdfBlob);\n        const a = document.createElement(\"a\");\n        a.href = url;\n        a.download = session?.documentName || \"document.pdf\";\n        document.body.appendChild(a);","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/hooks/workflow/useParticipantSession.ts#L80-L116","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nawait decline(token, reason);\n\n// after\ntry {\n  await decline(token, reason);\n} catch (e) {\n  showDeclineError(e instanceof Error ? e.message : 'Decline failed');\n}","handlingStrategy":"try-catch","validationCode":"if (!token) {\n  setError('Token is required to decline');\n  return;\n}","typeGuard":"export function isValidParticipantToken(token: string | undefined): token is string {\n  return typeof token === 'string' && token.length > 0;\n}","tryCatchPattern":"try {\n  await decline(token, reason);\n} catch (e) {\n  showDeclineError(e instanceof Error ? e.message : 'Decline failed');\n}","preventionTips":["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."],"tags":["workflow","decline","axios","participant","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}