{"record":{"id":"098cec5db9f13646","repo":"BigPizzaV3/CodexPlusPlus","slug":"app-098cec","errorCode":null,"errorMessage":"链接缺少解密密钥。请使用完整的分享链接。","messagePattern":"链接缺少解密密钥。请使用完整的分享链接。","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"services/share-site/app.js","lineNumber":96,"sourceCode":"    if (!response.ok) return setNotice(\"撤销失败，请稍后重试。\", \"error\");\n\n    localStorage.removeItem(`codexpp-share-delete:${id}`);\n    result.hidden = true;\n    setNotice(\"分享已撤销。\", \"success\");\n  });\n}\n\nasync function showShare(id) {\n  createView.hidden = true;\n  viewer.hidden = false;\n  const viewContent = document.querySelector(\"[data-view-content]\");\n  const viewMeta = document.querySelector(\"[data-view-meta]\");\n  const viewNotice = document.querySelector(\"[data-view-notice]\");\n  const importButton = document.querySelector(\"[data-import-session]\");\n\n  try {\n    const keyValue = new URLSearchParams(location.hash.slice(1)).get(\"k\");\n    if (!keyValue) throw new Error(\"链接缺少解密密钥。请使用完整的分享链接。\");\n\n    const response = await fetch(`/api/shares/${id}`, { cache: \"no-store\" });\n    const data = await response.json();\n    if (!response.ok) throw new Error(\"分享不存在、已撤销或已过期。\");\n\n    const plaintext = await decryptText(data.encrypted, keyValue);\n    try {\n      const parsed = JSON.parse(plaintext);\n      if (parsed?.kind === \"codex-rollout\" && typeof parsed.content === \"string\") {\n        sharedSession = parsed;\n        viewContent.textContent = rolloutToMarkdown(parsed.content, parsed.title);\n        importButton.hidden = false;\n        importButton.addEventListener(\"click\", importSharedSession, { once: true });\n      } else if (parsed?.kind === \"codex-session\" && Array.isArray(parsed.messages)) {\n        sharedSession = parsed;\n        viewContent.textContent = sessionToMarkdown(parsed);\n        importButton.hidden = false;\n        importButton.addEventListener(\"click\", importSharedSession, { once: true });","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/services/share-site/app.js#L78-L114","documentation":"showShare() extracts the decryption key from the URL fragment (location.hash, after '#') as query param 'k' before fetching the share payload from /api/shares/:id. Because the key is deliberately never sent to the server, a link missing '#k=...' cannot be decrypted, so the client throws this error immediately before any network call. It protects users from opening truncated links that were copy-pasted without the fragment.","triggerScenarios":"Opening a share URL whose hash fragment was stripped (e.g. copied from an address bar after page load, pasted through a chat app that strips fragments, or a redirect that drops the '#k=' part).","commonSituations":"Users share only the base URL /s/<id> without the key; HTTP->HTTPS or domain redirects drop the fragment since fragments are never sent to servers; browsers sometimes normalize/strip hashes when the page re-navigates.","solutions":["Ask the sender for the complete original link including the '#k=...' fragment and re-open it.","Paste the full URL directly into the address bar in one action instead of following an intermediate redirect or shortened link.","If you control the share flow, verify the share page preserves location.hash across any client-side routing before calling showShare."],"exampleFix":"// before\nconst keyValue = new URLSearchParams(location.hash.slice(1)).get(\"k\");\nif (!keyValue) throw new Error(\"链接缺少解密密钥。请使用完整的分享链接。\");\n// after\nconst keyValue = new URLSearchParams(location.hash.slice(1)).get(\"k\");\nif (!keyValue) {\n  viewNotice.textContent = \"链接缺少解密密钥，请使用含 #k= 片段的完整分享链接。\";\n  return;\n}","handlingStrategy":"validation","validationCode":"const keyValue = new URLSearchParams(location.hash.slice(1)).get(\"k\");\nif (!keyValue || !/^[A-Za-z0-9_-]+$/.test(keyValue)) {\n  viewNotice.textContent = \"链接缺少解密密钥，请使用完整的分享链接（含 #k= 片段）。\";\n  return;\n}","typeGuard":"function hasShareKey(url) {\n  return typeof url?.hash === 'string' && new URLSearchParams(url.hash.slice(1)).get('k') != null;\n}","tryCatchPattern":"try {\n  await showShare(id);\n} catch (e) {\n  if (e.message.includes(\"解密密钥\")) {\n    viewNotice.textContent = \"分享链接不完整：请使用包含 #k= 密钥片段的原始链接。\";\n  } else { throw e; }\n}","preventionTips":["Always copy the full URL including the '#k=' fragment; never copy from a redirected address bar.","Test share links end-to-end in a fresh incognito window before sending them.","Avoid routing layers that rewrite or drop the URL fragment.","Render a visible notice on the share page when the fragment key is absent instead of a bare throw."],"tags":["url","encryption","client-side","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}