Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#16504·joplin

Sync engine permanently deadlocks when Joplin Server returns HTTP 422 (Unprocessable Entity)

Author: Pcmhacker-piroCreated Sep 11, 2026Updated Sep 17, 2026
Labelsbugdesktophighv3.7

Operating system

macOS

Joplin version

3.7.18

Desktop version info

Joplin 3.7.18 (dev, darwin) Sync target: Joplin Server / Joplin Cloud Sync Version: 3

Current behaviour

When Joplin Server rejects an item upload with HTTP 422 (ErrorUnprocessableEntity)—such as when a note body contains an embedded null byte \0 introduced from a PDF/terminal/web clip paste—the client synchronization engine permanently crashes and enters an infinite failure loop.

Root cause analysis:

  1. In packages/lib/file-api-driver-joplinServer.ts:188-190:
    private isRejectedBySyncTargetError(error: { code?: number | string; httpCode?: number }) {
        return error.code === 413 || error.code === 409 || error.httpCode === 413 || error.httpCode === 409;
    }
    
    isRejectedBySyncTargetError only checks for HTTP 413 and 409, completely omitting HTTP 422.
  2. In packages/lib/Synchronizer.ts:809-819: Because 422 is not mapped to rejectedByTarget, handleCannotSyncItem() is bypassed, and the execution drops into else { throw error; }, which aborts the entire sync process.
  3. Because the item is never flagged as a disabled sync item, it stays in the sync queue. Every future sync (manual or automatic) tries to upload the same item, fails with HTTP 422, and aborts immediately.
  4. All synchronization across all user devices is completely blocked.

Expected behaviour

  1. isRejectedBySyncTargetError in packages/lib/file-api-driver-joplinServer.ts should include HTTP 422 so that unprocessable item rejections are recognized as rejectedByTarget.
  2. Synchronizer.ts should invoke handleCannotSyncItem() to record the unprocessable item into sync_disabled_items and present an actionable user notification, allowing the remaining notes to continue synchronizing smoothly without aborting the session.
  3. BaseModel.filter() should strip null bytes (\0) from string fields when saving notes locally to prevent unprocessable payloads from being generated in the first place.

Logs

Last error: Error: Item c9dade1fd48241d5a4b8783faa8e68ff.md cannot be saved because its body contains a null byte
Error code: 422

Source: laurent22/joplin

View original on GitHubView discussion on GitHub