Skip to content

desktop: "Images and files" picker never opens on Windows (Desktop IPC handler failed) #50153

Description

@NoFizz

Description

On OpenCode Desktop for Windows, choosing Images and files from the composer's + menu never opens the native file picker. A toast appears instead:

Request failed
Desktop IPC handler failed

The native dialog never appears, so the main-process handler throws before it can show anything. The desktop IPC client only produces that message when the handler exits with a defect (unhandled throw) rather than a typed error:

return n ? Error(`Desktop IPC handler failed`, { cause: n.defect }) : Error(`Desktop IPC request interrupted`)

The handler calls Electron's dialog directly, with no error mapping:

openFilePicker: D(`DesktopFiles.openFilePicker`)(function*(r, i) {
  let a = yield* ht(() => za.showOpenDialog({        // za = electron.dialog
    properties: [`openFile`, ...(i?.multiple ? [`multiSelections`] : [])],
    title: i?.title ?? W(`desktop.dialog.chooseFile`),
    defaultPath: i?.defaultPath,
    filters: gd(i?.extensions)
  }));
  ...

The renderer calls it with { defaultPath: <session directory>, multiple: true }.

Plugins

None. No opencode.json / opencode.jsonc in the project or in ~/.config/opencode (only service.json).

OpenCode version

Desktop app 2.0.11 (bundled CLI 2.0.11). The updater reports 2.0.11 is the latest available, so this is not a stale build.

Steps to reproduce

  1. Open OpenCode Desktop on Windows 11 (10.0.26200), connected to the local background server.
  2. Open any session.
  3. Click the + button at the bottom-left of the prompt input.
  4. Choose Images and files (Ctrl+U).
  5. The Windows file dialog never appears; the toast above is shown.

Reproduces consistently, including after a full reboot.

Screenshot and/or share link

No screenshots. The toast reads 请求失败 / Desktop IPC handler failed, triggered from the 图片和文件 (Ctrl+U) entry of the composer's + menu.

Operating System

Windows 11 Pro, 10.0.26200 (x64)

Terminal

N/A — desktop app (not the TUI)

Additional context

  • Drag & drop into the composer and pasting an image from the clipboard both work, as does typing a file path and letting the agent read it.
  • Other IPC paths (drafts, storage, readClipboardImage, getPathForFile) work, so the IPC bridge itself is healthy — only the showOpenDialog call fails.
  • No diagnostics to attach: no Crashpad dump, nothing in Windows Error Reporting, and the Defect branch of the IPC client rejects pending requests without logging, so main.log / renderer.log contain no stack trace.
  • Related: [Desktop] File attachment button not working on macOS 26.3 #14669 reports the same user-visible symptom on macOS (auto-closed for inactivity).
  • The 20 MB size guard in the same handler also throws inside the generator (desktop.picker.error.sizeLimit), so it appears to surface as the same generic message instead of its localized one.

Activity

  1. sunyongming commented on Sep 21, 2026

    @sunyongming
    Image { "e": {}, "t": { "_tag": "Exit", "requestId": 364, "exit": { "_tag": "Failure", "cause": [ { "_tag": "Die", "defect": "Expected string | null\n at [\"options\"][\"title\"]" } ] } } }

    Desktop V2.0.11、Windows 25H2、Minimum Reproducible Ctrl+U、Service status is normal.

  2. andogensi commented on Sep 21, 2026

    @andogensi

    I reproduced the options.title  decoding failure from the earlier comment against v2.0.11 using Effect 4.0.0-rc.112.

    For this failure, the request is rejected before the file-picker handler runs, so it does not establish a failure in Electron's showOpenDialog itself.

    The relevant path is:

    • renderer/platform/files.ts explicitly includes title: undefined and extensions: undefined when those options are absent.
    • renderer/ipc-client.ts posts the payload directly over a MessagePort, without the Effect RPC client's schema encoding. Structured clone preserves these properties.
    • main/ipc-transport.ts still uses codecFor: Schema.toCodecJson. For the picker schema, an omitted optional field is accepted, but an explicitly present undefined is rejected.

    Minimal reproduction, run from packages/desktop at that tag:

    import assert from "node:assert/strict"
    import { Schema } from "effect"
    import { FilesOpenFilePicker } from "./src/shared/ipc-rpc/files"
    
    const decode = Schema.decodeUnknownSync(Schema.toCodecJson(FilesOpenFilePicker.payloadSchema))
    assert.throws(
      () => decode({ options: { multiple: true, title: undefined, extensions: undefined } }),
      /Expected string \| null/,
    )
    assert.deepEqual(decode({ options: { multiple: true } }), { options: { multiple: true } })

    I also reproduced this through the actual IpcServerProtocolLive, RpcServer, production RPC schemas and a MessageChannel with a raw renderer-style request. The failure is:

    Expected string | null
      at ["options"]["title"]
    

    A local candidate fix omits undefined object properties recursively at the renderer's request boundary while preserving Uint8Array values. It leaves server-side validation intact. Avoid JSON-stringifying the whole payload: attachment bytes must remain binary.

    Before normalization, five regression cases failed (file picker, absent options, directory picker, save dialog and openPath without an application). With normalization, the focused suite passes 27 tests / 0 failures, including existing attachment size/authorization checks, multiple renderer ports and binary transfer:

    bun test src/main/ipc-transport.test.ts src/renderer/ipc-payload.test.ts src/renderer/platform/files.test.ts src/main/files

    The two ipc-payload files and additional transport cases are local additions, not present in the tag. Tests used Bun 1.4.2 and Effect/platform packages pinned to 4.0.0-rc.112.

    Verification limit: this is a source/transport-level reproduction and candidate fix; full desktop typechecking/build and native-dialog end-to-end verification remain incomplete locally. I have not verified a rebuilt packaged app.

    The current diff of #50207 improves error reporting but does not normalize request payloads, so it appears complementary to fixing this decoding failure.
    I am using a tool to translate Japanese into English, so I apologize if the meaning is incorrect.

  3. sbx45 commented on Sep 22, 2026

    @sbx45

    Confirming this on Windows 10.0.26200 with Desktop 2.0.12 (Spanish UI).

    I instrumented the desktop IPC error path in-place and the toast actually hides a chain of schema decode failures:

    1. Expected string | null at ["options"]["title"]
    2. After sending title: null instead of undefined: Expected array | null at ["options"]["extensions"]
    3. After sending null for both title and extensions in openAttachmentPickerDialog, Ctrl+U works completely.

    So the fix must normalize both optional fields (title and extensions) — openAttachmentPickerDialog sends title: t?.title, extensions: t?.extensions (both undefined when unset), while the request schema requires each field present as string | null / array | null.

    Useful workarounds until fixed: paste images with Ctrl+V and drag & drop files. Everything else (Configuration file button, opening the config without an existing opencode.json, etc.) behaves as described in #50503.

  4. Harshitttttttt commented on Sep 22, 2026

    @Harshitttttttt

    Can confirm does not work

  5. andogensi commented on Sep 22, 2026

    @andogensi

    Fix PR: #50333

    It addresses the undefined payload decoding issue at the renderer IPC request boundary, including both title and extensions, while preserving binary payloads.

    The fix has also been independently reproduced against the packaged Windows build, where applying the same normalization allowed Ctrl+U to open the native file picker.

  6. Hona commented on Sep 22, 2026

    @Hona
    Member

    Fixed in #50599 (merged to v2 as 8e62ad7). The renderer sent optional picker fields as undefined, which the main process's JSON schema codec rejected before the handler ran. The fix lands in the next desktop release after 2.0.12.

    Workaround until then: drag and drop or paste from the clipboard.

  7. villa-feng commented on Sep 22, 2026

    @villa-feng

    Confirming repro on Desktop 2.0.13 (Windows 11 Pro 10.0.26200, x64) — the fix from #50599 (8e62ad7, merged today) is not in the shipped 2.0.13 build, so the + menu → 图片和文件 ("Images and files") still fails with the toast 请求失败 / Desktop IPC handler failed. The native dialog never opens, consistent with the same undefined-field rejection path.

    Extra details from my machine that may confirm the closed-loop:

    1. Timing: the toast appears immediately after choosing the menu entry, before any dialog (showOpenDialog) appears.
    2. Confirmed workarounds on 2.0.13: drag & drop into the composer works fine (no error, file attaches); paste from clipboard also untested in my case but drop is verified.
    3. Silent-diagnostics point (matches desktop: file/folder pickers always fail on Windows — request schema rejects undefined for optional options.title / options.extensions ("Desktop IPC handler failed") #50503's instrumentation findings): nothing in main.log, renderer.log, or Crashpad when the failure happens — the Die exit of the shared Ut request boundary is never logged, so the toast is the only signal. The AppProtocolError branch does console.error('[desktop-ipc] protocol error', error), but a schema Fail at the renderer boundary never becomes one, which is why logging for failed desktop IPC requests would help future reports.
    4. app.getPath('documents') (onboarding) worked on this machine, unlike the secondary defect noted in desktop: file/folder pickers always fail on Windows — request schema rejects undefined for optional options.title / options.extensions ("Desktop IPC handler failed") #50503.

    Waiting for the next desktop release (> 2.0.13) that carries 8e62ad7; happy to verify the fix as soon as it ships.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions