{"record":{"id":"6c65f3c2c296c219","repo":"chatboxai/chatbox","slug":"slug-no-longer-owned","errorCode":null,"errorMessage":"slug no longer owned","messagePattern":"slug no longer owned","errorType":"http","errorClass":"VibedropSlugNotOwnedError","httpStatus":404,"severity":"warning","filePath":"src/renderer/packages/vibedrop.ts","lineNumber":104,"sourceCode":"}\n\nexport async function publishToVibedrop(params: PublishToVibedropParams): Promise<VibedropSite> {\n  const { html, vdKey, title, visibility, slug } = params\n  if (!html?.trim()) {\n    throw new Error('HTML content is empty, nothing to publish.')\n  }\n\n  const body: Record<string, unknown> = { html, visibility }\n  if (title) body.title = title\n  if (slug) body.slug = slug\n\n  const { status, json } = await postJson(`${VIBEDROP_API_ORIGIN}/v1/sites/inline`, body, vdKey)\n\n  if (status === 401 || status === 403) {\n    throw new VibedropAuthError(json?.error?.message || 'VibeDrop authorization failed')\n  }\n  if (status === 404 && json?.error?.code === 'slug_not_owned') {\n    throw new VibedropSlugNotOwnedError('slug no longer owned')\n  }\n  if (status >= 400 || !json?.site?.url) {\n    throw new Error(json?.error?.message || `Failed to publish to VibeDrop (status ${status})`)\n  }\n\n  return { slug: json.site.slug, url: json.site.url, visibility: json.site.visibility }\n}\n\n// ===== client-side caches (settings-persisted) =====\n\n// Decodes the email claim from the current account's JWT access token. Used to\n// bind the cached publish key to an account so it is never reused across\n// accounts (e.g. after switching login without an explicit logout).\nfunction currentAccountEmail(): string | null {\n  const token = authInfoStore.getState().accessToken\n  if (!token) return null\n  try {\n    const payload = token.split('.')[1]","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/vibedrop.ts#L86-L122","documentation":"Thrown as VibedropSlugNotOwnedError when the VibeDrop inline-publish endpoint returns 404 with error.code === 'slug_not_owned'. It signals that the requested slug was previously published but the current account/key no longer owns it (e.g. transferred, deleted, or owned by another account).","triggerScenarios":"Publishing with a slug that exists but is not owned by the current vdKey/account: postJson returns 404 and json.error.code === 'slug_not_owned'. The catch converts this into the typed error.","commonSituations":"User switched accounts and the cached slug belongs to the old account; slug was claimed by another user; re-publishing after a key/account reset where ownership changed; colliding slug across accounts.","solutions":["Publish without specifying the slug to let VibeDrop assign a new one.","Choose a different, unused slug.","If ownership is expected, re-verify the account/vdKey matches the one that originally created the slug."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Cannot pre-validate ownership without a server lookup.\n// Mitigation: avoid passing a slug, or let the user pick a fresh slug.","typeGuard":"function isVibedropSlugNotOwnedError(e: unknown): e is VibedropSlugNotOwnedError {\n  return e instanceof VibedropSlugNotOwnedError\n}","tryCatchPattern":"try {\n  await publishToVibedrop(params) // params.slug set\n} catch (e) {\n  if (e instanceof VibedropSlugNotOwnedError) {\n    // retry without params.slug, or ask user for a new slug\n  }\n}","preventionTips":["Let VibeDrop auto-assign slugs unless the user explicitly chooses one.","On account switch, drop any cached slug associations.","Surface this typed error distinctly so users can pick a new slug."],"tags":["vibedrop","publish","slug","ownership"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}