chatboxai/chatbox · warning · Error

Session attachment ${id} not found

Error message

Session attachment ${id} not found

What it means

Thrown by retrySessionAttachment when getSessionAttachment(id) returns null/undefined — i.e. no row exists for the given id. It is the precondition check before the status and reset logic. The numeric id is interpolated into the message.

Source

Thrown at src/main/session-attachment-rag/db.ts:668

  return false
}

export async function markSessionAttachmentFailed(id: number, error: string) {
  const client = getDatabase()
  const result = await client.execute({
    sql: 'UPDATE session_attachment SET status = ?, error = ?, processing_started_at = NULL, completed_at = NULL WHERE id = ? AND status != ?',
    args: ['failed', error, id, 'canceled'],
  })
  if ((result.rowsAffected || 0) > 0) {
    log.debug(`${SESSION_ATTACHMENT_RAG_LOG_PREFIX} [DB] Marked attachment failed: attachmentId=${id}, error=${error}`)
  }
}

export async function retrySessionAttachment(id: number) {
  const client = getDatabase()
  const existing = await getSessionAttachment(id)
  if (!existing) {
    throw new Error(`Session attachment ${id} not found`)
  }
  if (existing.status !== 'failed') {
    throw new Error('Only failed session attachments can be retried')
  }
  await client.execute({
    sql: 'UPDATE session_attachment SET status = ?, indexing_stage = ?, total_chunks = 0, embedded_chunks = 0, error = NULL, processing_started_at = NULL, completed_at = NULL WHERE id = ?',
    args: ['pending', 'queued', id],
  })
  log.debug(`${SESSION_ATTACHMENT_RAG_LOG_PREFIX} [DB] Reset attachment to pending: attachmentId=${id}`)
}

export async function cancelSessionAttachment(id: number) {
  const client = getDatabase()
  await client.execute({
    sql: 'UPDATE session_attachment SET status = ?, error = NULL, processing_started_at = NULL, completed_at = NULL WHERE id = ? AND status IN (?, ?, ?)',
    args: ['canceled', id, 'pending', 'indexing', 'failed'],
  })
  log.debug(`${SESSION_ATTACHMENT_RAG_LOG_PREFIX} [DB] Marked attachment canceled: attachmentId=${id}`)

View on GitHub (pinned to 81571269ad)

Solutions

  1. Validate the id still exists (getSessionAttachment) before showing a retry button, or ignore the retry if the row is gone.
  2. Ensure ids are passed as numbers (the SQL uses ? binding with a numeric id).
  3. If the row was intentionally deleted, surface 'attachment no longer exists' to the user instead of an error.
  4. Refresh the attachment list in the UI after deletes to prevent stale retry attempts.

Example fix

// before
const existing = await getSessionAttachment(id)
if (!existing) throw new Error(`Session attachment ${id} not found`)

// caller
try { await retrySessionAttachment(id) } catch (e) {
  if (/not found/.test((e as Error).message)) { /* remove from UI */ }
}
Defensive patterns

Strategy: validation

Validate before calling

const existing = await getSessionAttachment(id)
if (!existing) { ui.removeAttachment(id); return }

Type guard

function isAttachmentNotFound(e: unknown): e is Error { return e instanceof Error && /Session attachment \d+ not found/.test(e.message) }

Try / catch

try { await retrySessionAttachment(id) } catch (e) { if (isAttachmentNotFound(e)) { ui.removeAttachment(id); return } throw e }

Prevention

When it happens

Trigger: Calling retrySessionAttachment with an id that was never inserted, was hard-deleted, or whose id type does not match the column (e.g. passing a string '12' that the SQL layer does not coerce). Also after a DB reset/restore where the attachment row is gone but the UI still holds a stale id.

Common situations: Frontend retries an attachment whose row was deleted by cleanup; id passed from a cached list that is out of date; race between delete and retry; wrong id passed across IPC boundaries (string vs number).

Related errors


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