{"record":{"id":"160278ef5742a65b","repo":"Stirling-Tools/Stirling-PDF","slug":"operation-was-cancelled","errorCode":null,"errorMessage":"Operation was cancelled","messagePattern":"Operation was cancelled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"frontend/editor/src/core/hooks/tools/shared/useToolApiCalls.ts","lineNumber":108,"sourceCode":"            });\n            failedFiles.push(file.name);\n            try {\n              markFileError?.(file.fileId);\n            } catch (e) {\n              console.debug(\"markFileError\", e);\n            }\n            continue;\n          }\n          processedFiles.push(...responseFiles);\n          // record source id as successful\n          successSourceIds.push(file.fileId);\n          console.debug(\"[processFiles] Success\", {\n            name: file.name,\n            produced: responseFiles.length,\n          });\n        } catch (error) {\n          if (axios.isCancel(error)) {\n            throw new Error(\"Operation was cancelled\", { cause: error });\n          }\n          console.error(\"[processFiles] Failed\", { name: file.name, error });\n          failedFiles.push(file.name);\n          // mark errored file so UI can highlight\n          try {\n            markFileError?.(file.fileId);\n          } catch (e) {\n            console.debug(\"markFileError\", e);\n          }\n        }\n      }\n\n      if (failedFiles.length > 0 && processedFiles.length === 0) {\n        throw new Error(\n          `Failed to process all files: ${failedFiles.join(\", \")}`,\n        );\n      }\n","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/hooks/tools/shared/useToolApiCalls.ts#L90-L126","documentation":"Thrown in the per-file processing loop when `axios.isCancel(error)` is true — i.e. the request was aborted via the operation's cancelToken. This is not a backend failure; it is an intentional cancellation (user clicked Cancel, or the component holding the token unmounted). The original Cancel is preserved on `cause`.","triggerScenarios":"User clicks the Cancel button on a long-running operation (cancelOperation cancels cancelTokenRef.current.token); the component unmounts mid-operation and the parent cancels; an automation chain cancels a step.","commonSituations":"Cancelling a large multi-file compress/split; navigating away from the tool view while a batch is running; cancelling an automation step.","solutions":["Treat cancellation as expected control flow: catch it and stop without surfacing an error toast.","Ensure cancelOperation is wired to UI cancel affordances and called on component unmount via useEffect cleanup.","If cancellation fires unexpectedly, audit for accidental double-invocation of cancelToken.cancel (e.g. a stale ref).","Do not retry cancelled operations automatically — cancellation is user intent."],"exampleFix":"// before\nif (axios.isCancel(error)) {\n  throw new Error(\"Operation was cancelled\", { cause: error });\n}\n\n// after — propagate a distinct, non-error signal the UI can treat as expected\nif (axios.isCancel(error)) {\n  const cancelErr = new Error(\"Operation was cancelled\", { cause: error });\n  cancelErr.name = \"CancelledError\";\n  throw cancelErr;\n}\n// caller:\n} catch (e) {\n  if (e instanceof Error && e.name === \"CancelledError\") { onStatus(\"Cancelled\"); return; }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Do not invoke if the operation is already cancelled\nif (cancelTokenRef.current?.token.reason) {\n  // skip the call; the user already cancelled\n}","typeGuard":"function isCancelledError(e: unknown): boolean {\n  return e instanceof Error && e.message === \"Operation was cancelled\";\n}","tryCatchPattern":"} catch (e) {\n  if (e instanceof Error && (e.message === \"Operation was cancelled\" || axios.isCancel((e as { cause?: unknown }).cause))) {\n    // expected control flow — stop without an error toast\n    onStatus(\"Cancelled\");\n    return;\n  }\n  throw e;\n}","preventionTips":["Treat cancellation as expected flow; never surface it as an error toast.","Wire cancelOperation to UI affordances and component-unmount cleanup.","Do not auto-retry cancelled operations — cancellation is user intent."],"tags":["axios","cancellation","control-flow","react"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}