deepseek-ai/deepseek-harness · error

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

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.

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.

Appendix: 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)