{"record":{"id":"5ac77960fe25bf49","repo":"gildas-lormeau/SingleFile","slug":"saving-encrypted-multi-page-archives-is-not-suppor","errorCode":null,"errorMessage":"Saving encrypted multi-page archives is not supported yet.","messagePattern":"Saving encrypted multi-page archives is not supported yet\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/ui/bg/ui-editor.js","lineNumber":528,"sourceCode":"\teditorElement = previousEditorElement.cloneNode(true);\n\teditorElement.onload = () => {\n\t\teditorElement.onload = null;\n\t\tbrowser.runtime.sendMessage({ method: \"editor.getTabData\" });\n\t};\n\tpreviousEditorElement.replaceWith(editorElement);\n});\n\nasync function saveArchive(message) {\n\tconst options = tabData.options;\n\tconst manifest = message.manifest || {};\n\tconst manifestPages = manifest.pages || [];\n\tconst aliases = manifest.aliases || {};\n\tconst originalData = new Uint8Array(message.archiveContent);\n\tconst zipReader = new zip.ZipReader(new zip.Uint8ArrayReader(originalData));\n\tconst entries = await zipReader.getEntries();\n\tif (entries.some(entry => entry.encrypted)) {\n\t\tawait zipReader.close();\n\t\tthrow new Error(\"Saving encrypted multi-page archives is not supported yet.\");\n\t}\n\tconst entryMap = new Map(entries.map(entry => [entry.filename, entry]));\n\tconst modifiedPages = new Map(message.pages.map(page => [page.path, page]));\n\tconst pages = manifestPages.map(page => {\n\t\tconst modifiedPage = modifiedPages.get(page.path);\n\t\treturn {\n\t\t\turl: page.url,\n\t\t\toriginalUrls: page.originalUrls,\n\t\t\ttitle: modifiedPage ? modifiedPage.title : page.title,\n\t\t\tgetData: () => getPageData(page.path)\n\t\t};\n\t});\n\tconst zipScript = await (await fetch(\"/lib/single-file-zip.min.js\")).text();\n\tconst selfExtractingArchive = tabData.selfExtractingArchive !== undefined ?\n\t\ttabData.selfExtractingArchive :\n\t\t!(originalData[0] == 0x50 && originalData[1] == 0x4B);\n\tconst data = await zip.createPagesArchive(pages, {\n\t\tselfExtractingArchive,","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/gildas-lormeau/SingleFile/blob/517fb7c5cf2096d89933b747e862d8ecf616a9f9/src/ui/bg/ui-editor.js#L510-L546","documentation":"The background UI editor's saveArchive() refuses to persist an archive when zip.js reports any entry as encrypted. Writing modified pages back into an encrypted multi-page archive is not implemented (it would require decrypt/re-encrypt handling), so the code closes the ZipReader and throws this explicit feature-gap Error before any data is modified.","triggerScenarios":"saveArchive() receives message.archiveContent whose zip entries list contains at least one entry with entry.encrypted === true while the caller attempts to save modified pages into it via the editor save flow.","commonSituations":"User opens a password-protected ZIP-based document (e.g. an encrypted CBZ/ODF/zip archive) in the editor, edits pages, then hits save; archives produced by tools that encrypt entries by default; user supplied the wrong (unencrypted) copy but some entries remain encrypted.","solutions":["Remove password protection from the archive (open it with the password and re-save it unencrypted) before editing pages in the editor.","Decrypt the archive programmatically first (e.g. re-zip the entries without encryption) and feed the resulting unencrypted bytes to the editor.","Ask users to import an unencrypted copy of the document; surface this error in UI with guidance that encrypted archives are read-only.","Track upstream support for encrypted multi-page archive saving and upgrade when available."],"exampleFix":"// before\n// editing a password-protected archive then saving -> throws\nconst pages = await editor.save(encryptedArchiveBytes);\n// after\nconst decryptedBytes = await unzipWithoutPassword(encryptedArchiveBytes, userPassword);\nconst plainZip = await rezipUnencrypted(decryptedBytes);\nconst pages = await editor.save(plainZip);","handlingStrategy":"validation","validationCode":"import * as zip from '@zip.js/zip.js';\nexport async function assertNotEncrypted(uint8) {\n  const reader = new zip.ZipReader(new zip.Uint8ArrayReader(new Uint8Array(uint8)));\n  try {\n    const entries = await reader.getEntries();\n    if (entries.some(e => e.encrypted)) {\n      throw new Error('Archive is password-protected; decrypt before saving');\n    }\n  } finally { await reader.close(); }\n}","typeGuard":"function isEncryptedArchiveError(e) {\n  return e instanceof Error && e.message.includes('encrypted multi-page archives');\n}","tryCatchPattern":"try {\n  await editor.saveArchive(message);\n} catch (e) {\n  if (isEncryptedArchiveError(e)) {\n    showUserMessage('This document is password-protected. Remove the password before saving edits.');\n    return; // do not retry\n  }\n  throw e;\n}","preventionTips":["Detect entry.encrypted when opening an archive and switch the UI to read-only mode before edits.","Prompt for the archive password at import time and re-zip entries unencrypted for editing.","Reject password-protected uploads at ingestion rather than at save time.","Keep editors and zip.js updated in case encrypted-save support lands upstream."],"tags":["zip","encryption","unsupported-feature","archive"],"backgroundTag":"encrypted-archive-unsupported","analyzedSha":"517fb7c5cf2096d89933b747e862d8ecf616a9f9","analyzedAt":"2026-09-01T10:05:25.770Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}