chatboxai/chatbox · error · Error

Failed to publish to VibeDrop (status ${status})

Error message

Failed to publish to VibeDrop (status ${status})

What it means

Thrown by publishToVibedrop() as the catch-all when the response status is >= 400 (after auth and slug cases are handled) OR when status is 2xx but json.site.url is missing. It is the generic VibeDrop publish failure, preferring the server's error.message and falling back to a status-coded string.

Source

Thrown at src/renderer/packages/vibedrop.ts:107

  const { html, vdKey, title, visibility, slug } = params
  if (!html?.trim()) {
    throw new Error('HTML content is empty, nothing to publish.')
  }

  const body: Record<string, unknown> = { html, visibility }
  if (title) body.title = title
  if (slug) body.slug = slug

  const { status, json } = await postJson(`${VIBEDROP_API_ORIGIN}/v1/sites/inline`, body, vdKey)

  if (status === 401 || status === 403) {
    throw new VibedropAuthError(json?.error?.message || 'VibeDrop authorization failed')
  }
  if (status === 404 && json?.error?.code === 'slug_not_owned') {
    throw new VibedropSlugNotOwnedError('slug no longer owned')
  }
  if (status >= 400 || !json?.site?.url) {
    throw new Error(json?.error?.message || `Failed to publish to VibeDrop (status ${status})`)
  }

  return { slug: json.site.slug, url: json.site.url, visibility: json.site.visibility }
}

// ===== client-side caches (settings-persisted) =====

// Decodes the email claim from the current account's JWT access token. Used to
// bind the cached publish key to an account so it is never reused across
// accounts (e.g. after switching login without an explicit logout).
function currentAccountEmail(): string | null {
  const token = authInfoStore.getState().accessToken
  if (!token) return null
  try {
    const payload = token.split('.')[1]
    if (!payload) return null
    const json = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')))
    return typeof json.email === 'string' && json.email ? json.email.toLowerCase() : null

View on GitHub (pinned to 81571269ad)

Solutions

  1. Read the embedded server error.message — it usually states the specific validation/limit problem.
  2. For 429, wait and retry with backoff.
  3. For 5xx, retry after a short delay; if persistent, check VibeDrop status.
  4. If the body shape is unexpected, confirm the VibeDrop API version matches the client.
Defensive patterns

Strategy: retry

Validate before calling

function looksPublishable(status: number, json: unknown): boolean {
  return status < 400 && !!json && typeof (json as any)?.site?.url === 'string'
}

Type guard

function isVibedropPublishSuccess(json: unknown): json is { site: { slug: string; url: string; visibility: string } } {
  return !!json && typeof json === 'object' && typeof (json as any).site?.url === 'string'
}

Try / catch

try {
  await publishToVibedrop(params)
} catch (e) {
  if (e instanceof Error && /Failed to publish to VibeDrop/.test(e.message)) {
    // inspect server message; retry with backoff for 429/5xx
  }
}

Prevention

When it happens

Trigger: postJson returns any status >= 400 not already handled (e.g. 400 validation, 429 rate limit, 500), or a successful status whose body lacks json.site.url. The guard `if (status >= 400 || !json?.site?.url) throw ...` fires.

Common situations: Validation errors (invalid visibility/title), rate limiting, server-side 5xx, malformed response shape after a backend change, partial outage returning 200 with empty body.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/e667682be62e2513. Report an issue: GitHub.