{"record":{"id":"a0f9cf3044b80683","repo":"Foundry376/Mailspring","slug":"invalid-or-expired-preview-token","errorCode":null,"errorMessage":"Invalid or expired preview token","messagePattern":"Invalid or expired preview token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/browser/quickpreview-ipc.ts","lineNumber":104,"sourceCode":"  validateSender(event);\n  const filepath = getFilePath(event.sender.getURL());\n  const md = fs.readFileSync(filepath).toString();\n  return require('snarkdown')(md);\n};\n\n/**\n * Write preview data using an opaque token instead of a direct path.\n * The token must have been generated by generatePreviewToken().\n */\nconst finishWithData = (\n  event: IpcMainInvokeEvent,\n  previewToken: string,\n  arrayBuffer: ArrayBuffer\n) => {\n  validateSender(event);\n  const previewPath = previewTokens.get(previewToken);\n  if (!previewPath) {\n    throw new Error('Invalid or expired preview token');\n  }\n  // Token is single-use\n  previewTokens.delete(previewToken);\n  // Still validate the path as defense-in-depth\n  const resolvedPath = checkPathIsWithinFiles(previewPath);\n  fs.writeFileSync(resolvedPath, Buffer.from(arrayBuffer));\n};\n\n/**\n * Capture window screenshot using an opaque token instead of a direct path.\n * The token must have been generated by generatePreviewToken().\n */\nconst finishCapture = async (event: IpcMainInvokeEvent, previewToken: string) => {\n  validateSender(event);\n  const previewPath = previewTokens.get(previewToken);\n  if (!previewPath) {\n    throw new Error('Invalid or expired preview token');\n  }","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/Foundry376/Mailspring/blob/648c685d602ece6bb00c22534b8734de6ac644b3/app/src/browser/quickpreview-ipc.ts#L86-L122","documentation":"Guard in the quickpreview finishWithData IPC handler: the opaque previewToken submitted by the renderer has no entry in the in-memory previewTokens map — it was never issued, was already consumed (tokens are single-use), or the main process restarted and the map was lost.","triggerScenarios":"Thrown at app/src/browser/quickpreview-ipc.ts:104 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Request a fresh preview token via generatePreviewToken and retry the upload","If it recurs after app restart, ensure the renderer doesn't cache tokens across sessions since the map is process-local"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"648c685d602ece6bb00c22534b8734de6ac644b3","analyzedAt":"2026-09-03T02:00:24.311Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}