{"record":{"id":"45196f46c3cd3d85","repo":"Stirling-Tools/Stirling-PDF","slug":"file-not-found-in-storage-stub-name","errorCode":null,"errorMessage":"File not found in storage: ${stub.name}","messagePattern":"File not found in storage: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/hooks/useFileManager.ts","lineNumber":478,"sourceCode":"      };\n\n      const selectMultipleFiles = async (\n        files: StirlingFileStub[],\n        onStirlingFilesSelect: (stirlingFiles: StirlingFile[]) => void,\n      ) => {\n        if (selectedFiles.length === 0) return;\n\n        try {\n          // Filter by UUID and load full StirlingFile objects directly\n          const selectedFileObjects = files.filter((f) =>\n            selectedFiles.includes(f.id),\n          );\n\n          const stirlingFiles = await Promise.all(\n            selectedFileObjects.map(async (stub) => {\n              const stirlingFile = await fileStorage.getStirlingFile(stub.id);\n              if (!stirlingFile) {\n                throw new Error(`File not found in storage: ${stub.name}`);\n              }\n              return stirlingFile;\n            }),\n          );\n\n          onStirlingFilesSelect(stirlingFiles);\n          clearSelection();\n        } catch (error) {\n          console.error(\"Failed to load selected files:\", error);\n          throw error;\n        }\n      };\n\n      return {\n        toggleSelection,\n        clearSelection,\n        selectMultipleFiles,\n      };","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/hooks/useFileManager.ts#L460-L496","documentation":"Thrown inside `selectMultipleFiles` when `fileStorage.getStirlingFile(stub.id)` returns null for a selected stub. The stub is in UI state and selected, but its full StirlingFile blob is no longer in storage — so it cannot be handed to the consumer's onStirlingFilesSelect callback.","triggerScenarios":"Selected stubs were evicted from storage between selection and the 'use selected files' action; storage cleared mid-session; stale selection state restored pointing at deleted ids; concurrent operation deleted one of the selected files.","commonSituations":"User selects several files, waits, then acts after LRU eviction removed some; a remove-file action raced with selection; restored selection from a prior session.","solutions":["Filter missing files out and proceed with the available subset, warning the user which files were dropped, rather than failing the whole selection.","Validate selection against storage on selection change and prune unselectable stubs immediately.","Raise the LRU cap if eviction is the frequent cause.","Surface the missing file names in the error message for recoverability."],"exampleFix":"// before\nconst stirlingFiles = await Promise.all(\n  selectedFileObjects.map(async (stub) => {\n    const stirlingFile = await fileStorage.getStirlingFile(stub.id);\n    if (!stirlingFile) throw new Error(`File not found in storage: ${stub.name}`);\n    return stirlingFile;\n  }),\n);\n\n// after — drop missing, report which were skipped\nconst results = await Promise.all(\n  selectedFileObjects.map(async (stub) => ({ stub, file: await fileStorage.getStirlingFile(stub.id) })),\n);\nconst stirlingFiles = results.filter(r => r.file).map(r => r.file);\nconst missing = results.filter(r => !r.file).map(r => r.stub.name);\nif (stirlingFiles.length === 0) throw new Error(`No selected files are available in storage: ${missing.join(\", \")}`);\nif (missing.length) console.warn(\"Skipped missing files:\", missing);\nonStirlingFilesSelect(stirlingFiles);","handlingStrategy":"fallback","validationCode":"// Validate the selection against storage before acting\nconst checked = await Promise.all(\n  selectedFileObjects.map(async (stub) => ({ stub, ok: stub.id ? await fileStorage.hasFile?.(stub.id) : false })),\n);\nif (checked.some(c => !c.ok)) {\n  // warn which files are missing; proceed with available subset or abort\n}","typeGuard":"function isStirlingFile(f: StirlingFile | null | undefined): f is StirlingFile { return !!f; }","tryCatchPattern":"try {\n  await selectMultipleFiles(files, onStirlingFilesSelect);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"File not found in storage:\")) {\n    // list missing names, offer to re-add; proceed with available subset\n  } else throw e;\n}","preventionTips":["Validate selection against storage on selection change and prune unselectable stubs.","Drop missing files and proceed with the available subset instead of failing the whole selection.","Raise the LRU cap if eviction is the frequent cause."],"tags":["indexeddb","eviction","selection","file-storage","stale-state"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}