BigPizzaV3/CodexPlusPlus · error · Error

链接缺少解密密钥。请使用完整的分享链接。

Error message

链接缺少解密密钥。请使用完整的分享链接。

What it means

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.

Solutions

  1. Ask the sender for the complete original link including the '#k=...' fragment and re-open it.
  2. Paste the full URL directly into the address bar in one action instead of following an intermediate redirect or shortened link.
  3. If you control the share flow, verify the share page preserves location.hash across any client-side routing before calling showShare.

Example fix

// before
const keyValue = new URLSearchParams(location.hash.slice(1)).get("k");
if (!keyValue) throw new Error("链接缺少解密密钥。请使用完整的分享链接。");
// after
const keyValue = new URLSearchParams(location.hash.slice(1)).get("k");
if (!keyValue) {
  viewNotice.textContent = "链接缺少解密密钥,请使用含 #k= 片段的完整分享链接。";
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

const keyValue = new URLSearchParams(location.hash.slice(1)).get("k");
if (!keyValue || !/^[A-Za-z0-9_-]+$/.test(keyValue)) {
  viewNotice.textContent = "链接缺少解密密钥,请使用完整的分享链接(含 #k= 片段)。";
  return;
}

Type guard

function hasShareKey(url) {
  return typeof url?.hash === 'string' && new URLSearchParams(url.hash.slice(1)).get('k') != null;
}

Try / catch

try {
  await showShare(id);
} catch (e) {
  if (e.message.includes("解密密钥")) {
    viewNotice.textContent = "分享链接不完整:请使用包含 #k= 密钥片段的原始链接。";
  } else { throw e; }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/098cec5db9f13646. Report an issue: GitHub.

Appendix: source

Thrown at services/share-site/app.js:96

    if (!response.ok) return setNotice("撤销失败,请稍后重试。", "error");

    localStorage.removeItem(`codexpp-share-delete:${id}`);
    result.hidden = true;
    setNotice("分享已撤销。", "success");
  });
}

async function showShare(id) {
  createView.hidden = true;
  viewer.hidden = false;
  const viewContent = document.querySelector("[data-view-content]");
  const viewMeta = document.querySelector("[data-view-meta]");
  const viewNotice = document.querySelector("[data-view-notice]");
  const importButton = document.querySelector("[data-import-session]");

  try {
    const keyValue = new URLSearchParams(location.hash.slice(1)).get("k");
    if (!keyValue) throw new Error("链接缺少解密密钥。请使用完整的分享链接。");

    const response = await fetch(`/api/shares/${id}`, { cache: "no-store" });
    const data = await response.json();
    if (!response.ok) throw new Error("分享不存在、已撤销或已过期。");

    const plaintext = await decryptText(data.encrypted, keyValue);
    try {
      const parsed = JSON.parse(plaintext);
      if (parsed?.kind === "codex-rollout" && typeof parsed.content === "string") {
        sharedSession = parsed;
        viewContent.textContent = rolloutToMarkdown(parsed.content, parsed.title);
        importButton.hidden = false;
        importButton.addEventListener("click", importSharedSession, { once: true });
      } else if (parsed?.kind === "codex-session" && Array.isArray(parsed.messages)) {
        sharedSession = parsed;
        viewContent.textContent = sessionToMarkdown(parsed);
        importButton.hidden = false;
        importButton.addEventListener("click", importSharedSession, { once: true });

View on GitHub (pinned to b1ed92e5e4)