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
- 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.
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
- 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.
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
- API 地址须使用 HTTPS,且不含账号、密码、查询参数或片段
- API 地址须使用 HTTPS,且不含账号、密码、查询参数或片段
- Base URL 不能为空
- 上游 Base URL 不能为空
- 图片上游 Base URL 不能为空
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)