Skip to content

Document Linux USB mount-option workaround for OSError after Ctrl-D (#229)#491

Merged
makermelissa merged 1 commit intocircuitpython:mainfrom
makermelissa-piclaw:fix/issue-229-writeback
May 8, 2026
Merged

Document Linux USB mount-option workaround for OSError after Ctrl-D (#229)#491
makermelissa merged 1 commit intocircuitpython:mainfrom
makermelissa-piclaw:fix/issue-229-writeback

Conversation

@makermelissa-piclaw
Copy link
Copy Markdown
Contributor

Fixes #229.

What

On Linux, the default vfat mount options for the CIRCUITPY USB MSC drive defer writes via the kernel page cache. When CircuitPython is soft-rebooted (Ctrl+D) immediately after a save from the web editor, the device may see an inconsistent filesystem and report:

OSError: [Errno 5] Input/output error

This is a host-OS behavior, not a web-editor or firmware bug. macOS and Windows are unaffected. Network and Bluetooth workflows are also unaffected — only the USB workflow on Linux.

Why this approach

I spent a fair bit of time exploring code-side workarounds (createWritable({mode: "exclusive"}), post-write file re-open, REPL nudges, warm-up flushes, etc.). None of them were reliably fixing the bug — the writes really are sitting in the host kernel page cache for up to ~30 s, and the browser File System Access API does not expose any way to force fsync(). Verified empirically: remounting CIRCUITPY with -o sync makes the bug disappear in 3/3 trials. Restoring the default mount brings it back.

So instead of a fragile JS workaround, this PR documents the host-OS fix and surfaces it at the right moment — when a Linux user is opening the USB connect dialog.

Changes

  • js/common/utilities.js: add isLinux() helper. Uses navigator.userAgentData.platform first (modern Chromium) and falls back to userAgent, explicitly excluding Chrome OS and Android to avoid false positives.
  • index.html: add a collapsed <details> notice in the USB connect dialog's "Select USB Host Folder" step. Hidden by default; shown only when isLinux() is true.
  • js/workflows/usb.js: in showConnect(), query the notice element and toggle its hidden attribute based on isLinux().
  • sass/layout/_layout.scss: styling for the notice — soft-yellow info box with code-block formatting and a custom disclosure triangle.
  • docs/LINUX_USB_MOUNT.md: full guidance document covering:
    • What's happening (kernel page cache + vfat default async)
    • Quick fix (udisksctl unmount + udisksctl mount -o sync)
    • Permanent fix (udev rule using ENV{ID_FS_LABEL}=="CIRCUITPY" and UDISKS_MOUNT_OPTIONS+="sync")
    • Trade-offs

What this PR does not change

Verification

  • Linux (Pi 4B) on a CircuitPython board (RP2040): default mount → OSError on save+Ctrl-D; mount -o sync → no OSError in 3/3 trials.
  • Build clean (npm run build).
  • Notice renders correctly when isLinux() is true; hidden when false.
  • Verified the dialog is no longer scroll-clipped on a 720p viewport with the collapsed disclosure.

On Linux, the default vfat mount options for the CIRCUITPY USB MSC drive
defer writes via the kernel page cache. When CircuitPython is soft-rebooted
(Ctrl-D) immediately after a save, the device may see an inconsistent
filesystem and report OSError: [Errno 5] Input/output error.

This is a host-OS behavior, not a web-editor or firmware bug. macOS and
Windows are unaffected. Network and Bluetooth workflows are also unaffected.

Changes:
- Add isLinux() helper in js/common/utilities.js (excludes Chrome OS and
  Android to avoid false positives).
- Add a collapsible Linux-only notice in the USB connect dialog that
  surfaces the udisksctl remount commands.
- Add docs/LINUX_USB_MOUNT.md with full guidance, including a permanent
  udev rule using ID_FS_LABEL='CIRCUITPY' and UDISKS_MOUNT_OPTIONS+='sync'.

No code changes to the FSAPI client or REPL-based read-only fallback
path (circuitpython#327 behavior preserved).

Fixes circuitpython#229.
Copy link
Copy Markdown
Collaborator

@makermelissa makermelissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great

@makermelissa makermelissa merged commit c339d4a into circuitpython:main May 8, 2026
1 check passed
@dhalbert
Copy link
Copy Markdown
Contributor

dhalbert commented May 8, 2026

@makermelissa I am not sure this is a great long-term solution, because it is a big deal for the user to add the udev rule, etc. On Chromebooks, it is impossible for the average user.

The problem is more that the "File System Access API" doesn't provide an fsync, and we should lobby for that.,

I spent years making sure various editors do fsync's after file write: https://learn.adafruit.com/welcome-to-circuitpython/recommended-editors, https://github.com/adafruit/Atom-fsync-on-save, etc.

Since there is a mechanism in the web editor now for doing file edits through the REPL for boards that don't present CIRCUITPY, would an alternative be to detect the editor is running on Linux, and revert to REPL-based file writes?

@makermelissa
Copy link
Copy Markdown
Collaborator

The REPL based idea is worth exploring. It would require the user to eject the Circuit Python drive so it's not read only, but that's definitely a simpler solution. I'm out of hours this week, but I'll see if we can revert and take that approach instead.

@makermelissa
Copy link
Copy Markdown
Collaborator

Another option is possibly to just ignore the error. The writes do happen immediately and I discovered that waiting 30 seconds before reloading avoided the error altogether.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

writing code.py on Linux causes: OSError: [Errno 5] Input/output error

3 participants