Skip to content

fix(db): encode new leaf names in create/rename so spaces are accepted - #820

Open
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:fix/db-encode-create-rename-name
Open

fix(db): encode new leaf names in create/rename so spaces are accepted#820
joewiz wants to merge 2 commits into
eXist-db:developfrom
joewiz:fix/db-encode-create-rename-name

Conversation

@joewiz

@joewiz joewiz commented Jun 10, 2026

Copy link
Copy Markdown
Member

[This PR was co-authored with Claude Code. -Joe]

Problem

Creating a collection or renaming a resource/collection to a name containing a space fails with HTTP 400:

POST /api/storage/db/foo  {"action":"create","name":"My Folder"}
→ 400  "Invalid value for cast/constructor. failed to convert My Folder
        into an XmldbURI: Illegal character in path at index 2: My Folder"

The create and rename handlers in modules/api/db.xqm took the new leaf name straight from the request body and passed it un-encoded to xmldb:create-collection / xmldb:rename — while every path elsewhere in the module already goes through db:encode-path. Those xmldb:* functions reject a raw space (FORG0001), so spaces in new names never worked. (Non-ASCII names happened to work because the xmldb:* functions escape those internally.)

Fix

Encode the new leaf name with the module's existing db:encode-path helper, exactly like every other path the module handles:

  • create: let $name := db:encode-path($body?name)
  • rename: let $target := db:encode-path($body?target)

Verification (live instance)

action before after
create My Folder 400 200 → stored My%20Folder, listed as My Folder
rename → renamed file.xml 400 200 → stored renamed%20file.xml, listed as renamed file.xml

The listing decodes for display as before, so the round-trip (create with a space → shows the space) is clean. This keeps the new leaf consistent with the module's existing full-encode convention; it does not change how any other name is stored.

@duncdrum duncdrum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@joewiz while we are working on a fix in core, we should add a regression guard here in exide

@duncdrum

Copy link
Copy Markdown
Contributor

@joewiz could add a test to guard against regressions?

@line-o

line-o commented Aug 17, 2026

Copy link
Copy Markdown
Member

@joewiz could you revisit this change?

joewiz and others added 2 commits August 18, 2026 23:44
The create and rename handlers took the new collection/resource name raw
from the request body and passed it straight to xmldb:create-collection /
xmldb:rename, while every path elsewhere in the module goes through
db:encode-path. Those functions reject a raw space ("Illegal character in
path", FORG0001), so creating a collection named "My Folder" or renaming
to "report final.xml" failed with HTTP 400.

Encode the leaf name with db:encode-path, like every other path in the
module. Verified against a live instance: create "My Folder" and rename to
"renamed file.xml" now return 200 (stored My%20Folder / renamed%20file.xml,
displayed decoded in the listing). Non-ASCII names were already handled by
the xmldb:* functions; this closes the space case and keeps the leaf
consistent with the module's existing full-encode convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Duncan asked for a regression guard on this PR (eXide#820). Before the fix,
`db:post` passed the new leaf name to xmldb:create-collection / xmldb:rename
un-encoded while every path in the module already went through db:encode-path,
so a space came back as 400 "Illegal character in path". Nothing in the suite
covered it: dbmanager_spec renames to AéB, and the xmldb:* functions escape
non-ASCII internally, so only a space (or another URI-illegal ASCII character)
exposes the gap.

The new spec covers all three call sites the fix touches — create, the
collection branch of rename, and the resource branch of rename — plus a
create/delete round trip through the DB manager UI, and asserts the listing
shows the decoded name so a space survives storage as %20 and comes back.

Verified against eXist-db 7.0.0-SNAPSHOT with existdb-openapi 0.10.0: 3 of the
4 tests fail on develop without this PR's fix (the create assertion reports the
exact "Illegal character in path" 400), all 4 pass with it, and the full suite
is unaffected.

Requires develop's cy.execXQuery support command, hence the merge of develop
into this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@joewiz
joewiz force-pushed the fix/db-encode-create-rename-name branch from 32939a6 to 436456b Compare August 19, 2026 03:49
@joewiz

joewiz commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

@duncdrum @line-o — regression guard added, and I re-checked the premise of the fix against a current build before doing so.

Does eXist-db still need eXide to encode the name?

Yes. On today's develop build (7.0.0-SNAPSHOT, build 2026-08-18, revision 61a9b9e):

xmldb:create-collection("/db", "Space Test A")
(: → Invalid value for cast/constructor. failed to convert Space Test A
      into an XmldbURI: Illegal character in path at index 5 :)

xmldb:create-collection("/db", xmldb:encode("Space Test B"))
(: → /db/Space%20Test%20B :)

So the core behavior this PR works around is unchanged, and the one-line encode is still what makes a space work. It also stays correct if core is later changed under eXist-db/exist#3795: eXide passes the raw name the user typed, so there is nothing to double-encode, and the listing already decodes for display.

The regression guard

New spec cypress/e2e/db_names_with_spaces_spec.cy.js, covering all three call sites the fix touches — create, the collection branch of rename, and the resource branch of rename — plus a create/delete round trip through the DB manager UI. Each case asserts the round trip, not just the status: the listing must show the decoded name, so a space survives storage as %20 and comes back as a space.

Measured both ways against eXist-db develop with existdb-openapi 0.10.0:

  • without the fix: 3 of the 4 tests fail, and the create assertion reports the exact 400 — "failed to convert My Folder into an XmldbURI: Illegal character in path at index 2"
  • with the fix: 4 of 4 pass, and the full suite is otherwise unchanged

One honest caveat: the resource-rename case passes even without the fix, because xmldb:rename($collection, $name, $target) tolerates a raw space where the two-argument collection form does not. I kept it as a round-trip guard rather than dropping it, since it pins the behavior we actually want from the API.

Worth noting why nothing caught this before: dbmanager_spec renames to AéB, and the xmldb:* functions escape non-ASCII internally — so only a space (or another URI-illegal ASCII character) exposes the gap.

The branch is rebased on current develop and is now two commits: the fix and the guard.

@joewiz

joewiz commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

[This response was prompted by Joe, drafted by Claude Code, and reviewed by Joe.]

The failing CI test here is not from this PR. CI installs a pinned roaster-1.12.0, which against the current eXist-db image cannot serialize eXide's login response, so POST /api/auth/session returns 400 and every spec fails at login — on this branch, on #814, on #866, and on plain develop when reproduced locally. The details and the version isolation are in the note on #814: #814 (comment)

#865 fixes CI by deploying the packages the eXist-db image bundles rather than pinned ones. On that stack this PR's suite runs clean: the regression guard added here passes 4 of 4 against eXist-db 7.0.0-SNAPSHOT with existdb-openapi 0.10.0, and fails 3 of 4 without the fix.

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.

3 participants