deepseek-ai/deepseek-harness · error

conversation.sendSession: one or more draft images are no lo

Error message

conversation.sendSession: one or more draft images are no longer available

What it means

sendSession() resolves the submitted draft-image ids against the runtime-owned draftAttachments map. Draft attachments are runtime-only: ids released by a prior successful submit (releaseDraftImages), removed from the composer, or lost to a remount simply no longer resolve. Any length mismatch throws before upload or prompt, so nothing is partially sent.

Source

Thrown at packages/client/ui-conversation/src/client/service.ts:154

  /**
   * Submit ordered draft images with text through one host admission.
   * @param session - target session.
   * @param text - serialized prompt text.
   * @param imageIds - ordered draft-local attachment ids.
   * @param mode - queue or steer delivery selected by composer policy.
   * @param signal - optional cancellation for the complete Host admission.
   * @returns the Host admission outcome; local attachment preparation failures reject.
   */
  async sendSession(
    session: SessionFace,
    text: string,
    imageIds: readonly DraftAttachmentId[],
    mode: InputSubmitMode,
    signal?: AbortSignal,
  ): Promise<SubmitOutcome> {
    const attachments = this.draftImages(imageIds)
    if (attachments.length !== imageIds.length) {
      throw new Error('conversation.sendSession: one or more draft images are no longer available')
    }
    const uploaded = await this.serializeImages(attachments.map(attachment => attachment.file))
    const content = [...uploaded, ...(text === '' ? [] : [{ type: 'text' as const, text }])]
    const result = await session.prompt(content, mode, signal)
    if (!result.ok) return { kind: 'error' }
    this.releaseDraftImages(attachments)
    return { kind: 'success' }
  }

  /**
   * Create runtime-only draft images and their object URLs.
   * @param files - browser files to register after MIME validation.
   * @returns ordered draft descriptors.
   */
  createDraftImages(files: readonly File[]): readonly ComposerAttachment[] {
    for (const file of files) imageMediaType(file.type)
    return files.map((file) => {
      const attachment = browserDraftAttachment(file)

View on GitHub (pinned to b150a551b8)

Solutions

  1. Reconcile ids against live attachments before submitting (draftImages(ids).length check) and re-attach files when short
  2. Latch Enter while a submit attempt is pending so double-submit cannot occur
  3. On remount, rebuild draft attachments from the File objects instead of reusing stale ids

Example fix

// before
await service.sendSession(session, text, imageIds, mode)
// after
if (service.draftImages(imageIds).length !== imageIds.length) {
  imageIds = currentComposerIds() // or block the submit with a notice
}
await service.sendSession(session, text, imageIds, mode)
Defensive patterns

Strategy: validation

Validate before calling

if (service.draftImages(imageIds).length !== imageIds.length) {
  imageIds = liveComposerIds()
  if (imageIds.length === 0) return { kind: 'error' }
}
await service.sendSession(session, text, imageIds, mode)

Try / catch

try {
  await service.sendSession(session, text, imageIds, mode)
} catch (error) {
  if (isDraftImageGone(error)) refreshComposerAttachments() // re-attach and let the user retry
  throw error
}

Prevention

When it happens

Trigger: Submitting the same image ids twice — the first success already released them via releaseDraftImages; removing an image chip while its submit is in flight; or using ids captured before a remount (HMR, panel re-mount) against a fresh runtime that never registered them.

Common situations: Double-Enter races in the composer; X-ing an image mid-submission; hot reload landing between draft persistence and send.

Related errors


AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24). Data as JSON: /api/errors/484af0af42e15a72. Report an issue: GitHub.