{"record":{"id":"8ea09e6f12a8bc07","repo":"Stirling-Tools/Stirling-PDF","slug":"no-files-provided-for-sharing","errorCode":null,"errorMessage":"No files provided for sharing.","messagePattern":"No files provided for sharing\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/editor/src/core/services/serverStorageBundle.ts","lineNumber":120,"sourceCode":"    compressionOptions: { level: 6 },\n  });\n\n  const firstStubName = allStubs[0]?.stubs[0]?.name || \"shared\";\n  const rootName = sanitizeFilename(firstStubName);\n  const bundleFile = new File([zipBlob], `${rootName}-history.zip`, {\n    type: \"application/zip\",\n    lastModified: Date.now(),\n  });\n\n  return { bundleFile, manifest };\n}\n\nexport async function buildSharePackage(stubs: StirlingFileStub[]): Promise<{\n  bundleFile: File;\n  manifest: ShareBundleManifest;\n}> {\n  if (stubs.length === 0) {\n    throw new Error(\"No files provided for sharing.\");\n  }\n\n  const zip = new JSZip();\n  const entries: ShareBundleEntry[] = [];\n\n  for (const stub of stubs) {\n    const file = await fileStorage.getStirlingFile(stub.id as FileId);\n    if (!file) {\n      throw new Error(`Missing file data for ${stub.name || stub.id}`);\n    }\n\n    const logicalId = stub.id as string;\n    const filePath = `files/${logicalId}/${sanitizeFilename(stub.name || \"file\")}`;\n    const buffer = await file.arrayBuffer();\n    zip.file(filePath, buffer);\n\n    entries.push({\n      logicalId,","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/services/serverStorageBundle.ts#L102-L138","documentation":"`buildSharePackage` throws immediately if the caller passes an empty `stubs` array. This is pure input validation — the caller (UI) invoked the share flow with nothing selected.","triggerScenarios":"The share button/action was invoked when no files are selected, the selection was cleared between the click and the call, or a filter reduced the stubs list to zero before sharing.","commonSituations":"Share button not disabled in an empty state; a race where the selection is cleared (e.g. by a re-render) before `buildSharePackage` runs; upstream code computes `stubs` incorrectly and yields `[]`.","solutions":["Disable the Share action in the UI whenever the selection is empty.","In the caller, guard `if (stubs.length === 0) return notify('Select at least one file to share.')` before invoking.","Add a type-guard so `buildSharePackage` only receives a non-empty array at the type level."],"exampleFix":"// before\nif (stubs.length === 0) {\n  throw new Error(\"No files provided for sharing.\");\n}\n\n// caller-side guard\nasync function share(stubs: StirlingFileStub[]) {\n  if (stubs.length === 0) {\n    notifyUser(\"Select at least one file to share.\");\n    return;\n  }\n  return buildSharePackage(stubs);\n}","handlingStrategy":"validation","validationCode":"// Guard before invoking the share flow\nif (!stubs || stubs.length === 0) {\n  notifyUser(\"Select at least one file to share.\");\n  return;\n}\nawait buildSharePackage(stubs);","typeGuard":"function hasItems<T>(arr: T[]): arr is [T, ...T[]] {\n  return arr.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Disable the Share action in the UI whenever the selection is empty.","Guard for an empty array in the caller before invoking buildSharePackage.","Use a non-empty-array type guard so the compiler enforces the precondition."],"tags":["validation","sharing","storage","precondition"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}