deepseek-ai/deepseek-harness · warning

notice.imagesUnsupported

Error message

notice.imagesUnsupported

What it means

matchEnter() adjudicates an Enter keypress on a slash-command token. When the command's live contribution is available in this session, the token is bare (no arguments after the command name), and the submission envelope carries image attachments, the localized notice key 'notice.imagesUnsupported' is thrown via refuseImages(): popup-style slash commands take no images, and the composer keeps the draft plus images for the user to fix.

Source

Thrown at packages/client/ui-commands/src/client/service.ts:332

   * popup, non-accepting claim, bare detached execute — throws the refusal
   * so the machine surfaces one composer notice and the draft and images
   * stay in place; nothing executes and nothing is dropped.
   */
  private async matchEnter(
    session: ClientSessionContext,
    line: string,
    signal: AbortSignal,
    envelope: SubmitEnvelope,
  ): Promise<PickOutcome> {
    const trimmed = line.trim()
    if (!trimmed.startsWith('/')) return undefined
    const ws = trimmed.search(/\s/)
    const token = ws === -1 ? trimmed : trimmed.slice(0, ws)
    const bare = ws === -1
    const name = token.slice(1)
    if (name === '') return undefined
    const refuseImages = (): never => {
      throw new Error(this.t('notice.imagesUnsupported', { command: name }))
    }
    const contribution = this.live.contributions.get(name)
    if (contribution !== undefined && contribution.available(session)) {
      if (!bare) return undefined
      if (envelope.images > 0) refuseImages()
      this.openPopup(name, contribution.ui, session, { via: 'enter', token })
      return 'handled'
    }
    await this.directory.ensureReady(session.sessionId, signal)
    const desc = this.directory.resolve(session.sessionId, name)
    if (desc === undefined) return undefined
    // Bare enter on a decorated host command opens its popup; an argued line
    // never consults the decoration (the claim/detached paths below own it).
    if (bare) {
      const decoration = this.live.decorations.get(name)
      if (decoration !== undefined && decoration.available(session)) {
        if (envelope.images > 0) refuseImages()
        this.openPopup(name, decoration.ui, session, { via: 'enter', token })

View on GitHub (pinned to b150a551b8)

Solutions

  1. Remove the image attachments from the composer (or send them with a plain-text prompt instead) and press Enter again
  2. Add arguments after the command so the token is not bare, when the command accepts them
  3. As a plugin author, give the command an images-capable handler instead of a popup UI if it should accept images
Defensive patterns

Strategy: validation

Validate before calling

const token = trimmed.split(/\s/, 1)[0] ?? ''
const isBareSlash = token.startsWith('/') && token.length > 1 && trimmed === token
if (isBareSlash && envelope.images > 0) {
  // strip the images or warn before calling matchEnter
}

Try / catch

try {
  handleEnter(service.matchEnter(session, envelope))
} catch (error) {
  if (error instanceof Error && error.message === t('notice.imagesUnsupported', { command: name })) {
    showNotice(error.message) // draft and images are retained by design
    return
  }
  throw error
}

Prevention

When it happens

Trigger: The user attaches or pastes images, then presses Enter on a bare recognized slash command (for example /model or /permission) whose contribution opens a popup — envelope.images > 0 with a bare token triggers refuseImages().

Common situations: Pasting a screenshot and then Enter-ing a settings slash command; drag-dropping an image while the composer already holds a bare command token; automated submit pipelines that always attach images regardless of draft content.

Related errors


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