Skip to content

Make WebDAV PUT idempotent via auto-materialisation of missing parents#58

Merged
TheZupZup merged 1 commit into
mainfrom
claude/fix-webdav-conflict-errors-9bvOh
May 3, 2026
Merged

Make WebDAV PUT idempotent via auto-materialisation of missing parents#58
TheZupZup merged 1 commit into
mainfrom
claude/fix-webdav-conflict-errors-9bvOh

Conversation

@TheZupZup
Copy link
Copy Markdown
Owner

Summary

This PR fixes a critical issue where sync push operations would fail with 409 errors when notes had non-ASCII characters in their titles (e.g., "Chaudré de saucisses", "Commande d'imprimante"). The root cause was that URL-encoded slugs in PROPFIND responses weren't being decoded, causing the client to think parent collections didn't exist and triggering redundant MKCOL requests.

The fix implements two complementary strategies:

  1. Auto-materialisation of missing parents: When a PUT request targets a path whose ancestors don't exist, the server now transparently creates the missing notebook and note from their slugs (if they carry valid id-prefixes). This makes PUT idempotent and eliminates spurious 409 failures.

  2. URL decoding in PROPFIND parsing: The client now properly decodes URL-encoded hrefs returned by PROPFIND, so notes with accented characters are correctly recognized as already existing.

Key Changes

Server-side (webdav_provider.py)

  • Modified get_resource_inst() to auto-materialize missing parent collections when handling PUT requests
  • Added _materialize_notebook() helper to create notebooks from slugs with valid id-prefixes
  • Added _materialize_note() helper to create notes with placeholder ids from slugs
  • Strict validation: only materializes when slug carries a valid id-prefix; returns 409 for slugs without id-prefixes to surface client bugs

Client-side (client.py)

  • Fixed _parse_propfind() to URL-decode href paths before extracting resource names
  • This ensures slugs with non-ASCII characters (which get percent-encoded in HTTP responses) are correctly matched against local slugs

Test coverage (test_webdav_sync_push.py)

  • Renamed and refactored test_put_into_missing_parent_returns_409_not_500 to test_put_into_missing_parent_auto_materialises with full validation
  • Added test_put_with_no_id_prefix_still_fails to ensure 409 is still returned for invalid slugs
  • Added comprehensive TestIdempotentPush class with 5 new tests covering:
    • Re-uploading unchanged notes
    • Overwriting modified notes
    • Direct PUT on existing resources
    • Repeated pushes with accented titles
    • PROPFIND name decoding validation

Implementation Details

  • Auto-materialisation only activates for PUT requests (read methods remain strict)
  • Notebooks/notes are created with placeholder ids derived from slug prefixes; the actual client id is applied during the subsequent PUT of note.json
  • Logging added for materialized resources to aid debugging
  • All materialisation failures are caught and logged; the operation gracefully falls back to returning None (which triggers the normal 409 response)

https://claude.ai/code/session_01LsksuvVsUfJgTExHMXYUuw

PUT requests now auto-materialise missing notebook/note collections from
their slugs (id-prefix) so a partial MKCOL chain or a slug-encoding
mismatch no longer aborts the upload. PROPFIND name comparison is also
URL-decoded client-side so notes with non-ASCII titles (e.g. 'Chaudré',
'Commande d'imprimante…') are recognised as already existing.

https://claude.ai/code/session_01LsksuvVsUfJgTExHMXYUuw
@TheZupZup TheZupZup merged commit 3cedaef into main May 3, 2026
1 check passed
@TheZupZup TheZupZup deleted the claude/fix-webdav-conflict-errors-9bvOh branch May 3, 2026 16:40
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a931cbe231

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +854 to +855
if auto_materialize and len(parts) >= 3:
target_note = _materialize_note(self.db, target_nb, parts[1])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Defer parent auto-creation until PUT target is validated

get_resource_inst() now creates missing notebook/note collections for every PUT path with at least three segments, before confirming the child resource is actually writable (e.g., note.json or page_N.ink) and before payload validation runs. With WsgiDAV’s do_PUT flow, a request like PUT /nb__deadbeef/note__cafebabe/unsupported.bin (or malformed note.json) will still persist a new notebook/note, then fail with 403/400, leaving orphan data from a failed write. This is a regression in data integrity because failed requests now mutate storage.

Useful? React with 👍 / 👎.

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.

2 participants