Skip to content

Fix ui.upload unregisters wrong url on delete (#6236) - #6256

Open
SaadZahem wants to merge 1 commit into
zauberzeug:mainfrom
SaadZahem:bug/upload-url-leak#6236
Open

Fix ui.upload unregisters wrong url on delete (#6236)#6256
SaadZahem wants to merge 1 commit into
zauberzeug:mainfrom
SaadZahem:bug/upload-url-leak#6236

Conversation

@SaadZahem

@SaadZahem SaadZahem commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Motivation

Fixes #6236

Implementation

Introduced a new variable: upload._registered_url.
I tested the the change using the example provided in #6236.
For the sake of being explicit, here is the output of that script

nicegui auto-registered route still registered (leaked): False
application route /api/my-upload still registered:       True

I am not sure whether writing a pytest for this case is necessary, so I will leave that up for the reviewer to decide.

Progress

  • The PR title is a short phrase starting with a verb like "Add ...", "Fix ...", "Update ...", "Remove ...", etc.
  • The implementation is complete. (Otherwise, open a draft PR.)
  • This PR does not address a security issue. (Security fixes must be coordinated via the security advisory process before opening a PR.)
  • Pytests have been added/updated, or the Implementation section explains why they are not necessary.
  • Documentation has been added/updated or is not necessary.
  • No breaking changes to the public API or migration steps are described above.

@SaadZahem
SaadZahem marked this pull request as ready for review August 6, 2026 18:03

@evnchn evnchn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Posted by Claude Code on @evnchn's behalf.

Approving — I ran both repros from issue 6236 against main and against this branch: both symptoms flip. Snapshotting the path you actually registered, instead of re-reading a user-writable prop at teardown, is the right shape for this. Two zero-cost tightenings below, neither blocking.

Empirical before/after, and two optional tightenings

Base 3bb58aee (main) vs. this branch 4cfeb3d0, same interpreter, selected via PYTHONPATH; each run printed nicegui.__file__ to confirm which tree was loaded.

Repro 1.props('url="/api/my-upload"') then delete()

base this PR
NiceGUI's own upload route leaked True False
app's own /api/my-upload survived False True

Repro 2.props(remove='url') then card.delete()

base this PR
teardown raised KeyError 'url' completed
upload still in client.elements True False
sibling created after it stranded, is_deleted=False gone, is_deleted=True

Repro 2 is the one that earns the fix on its own — the exception escaped _handle_delete into Client.remove_elements, whose loop is unguarded, so every element after the upload in deletion order was stranded too.

Also run on this branch, since CI was still queued at the time of writing: tests/test_upload.py → 17 passed / 0 skipped; mypy ./nicegui → clean over 245 files; pylint ./nicegui → 10.00/10; pre-commit run --all-files → all hooks pass.

Optional, non-blocking:

  1. Line 75 still registers with self._props['url'] while line 127 removes with self._registered_url. Identical today — I checked that class-level default props are seeded in Element.__init__ and then overwritten by line 61, so both sites see the same path even for a subclass carrying default_props='url=...'. But nothing structurally enforces the pairing: anything that later touches the prop between lines 62 and 75 would register one path and unregister another, which is this bug again. Passing self._registered_url to @app.post(...) costs no extra lines and makes the pair unbreakable.

  2. A class-level _registered_url: str = '' default would close the last teardown-raises path: if __init__ aborts after Element.__init__ has already registered the element but before line 62 runs, _handle_delete now raises AttributeError where it used to raise KeyError. Pre-existing in kind rather than anything this PR introduces — base fails the same way — but it is the same strand-the-siblings failure the PR is fixing.

On tests: CONTRIBUTING asks that a fix deliberately shipping without one say why in the Implementation section. Both symptoms here are assertable on observable behavior without touching internals — delete a container holding an upload whose url prop was changed, then assert a sibling element is actually gone — roughly the shape of the existing test_replace_upload. Either a test or a line in the PR body would close that out; entirely the maintainers' call, not something I'd hold the fix for.

@falkoschindler
falkoschindler self-requested a review August 6, 2026 19:35
@falkoschindler falkoschindler self-assigned this Aug 6, 2026
@falkoschindler falkoschindler added bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review labels Aug 6, 2026
@falkoschindler falkoschindler added this to the Next milestone Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Type/scope: Incorrect behavior in existing functionality review Status: PR is open and needs review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui.upload unregisters whatever url currently points at on delete, so changing the prop leaks its own route and removes the app's

3 participants