Skip to content

fix(server): keep a deleted server's notifier out of error state - #1520

Merged
GT-610 merged 1 commit into
mainfrom
fix/server-delete-live-notifier
Sep 19, 2026
Merged

GT-610 merged 1 commit into
mainfrom
fix/server-delete-live-notifier

Conversation

@GT-610

@GT-610 GT-610 commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

What

Deleting a server left its serverProvider in an error state, and the app logged Unhandled (FlutterError): Bad state: Server ... not found twice per delete (#1512).

Why

ServerNotifier is @Riverpod(keepAlive: true), so ref.invalidate does not dispose the element. In Riverpod 3 invalidateSelf runs the dispose listeners and then schedules a refresh, and _performRefresh flushes an element that is isActive — one something is still watching. The server editor does exactly that, and it is the page the delete button sits on.

So after _delServer removed the record, build ran again, found no SPI and threw. The same shape is in the rename path (_updateServer) and in reload() after a sync pull drops a record — both invalidate an id that is already gone.

The dispose listener also read state to close the SSH client. Reading the state there forces that same rebuild, and the throw came before the close, so the connection was left open too.

How

  • build keeps the state it last had when the record is gone: the readings it already had, marked disconnected, client dropped. Nothing polls it again (refresh walks the servers that still exist), and a record restored under the same id is picked up by the lookup on the next rebuild. A build for an id that never existed still throws.
  • The SSH client is closed from a field that every path installing or dropping one keeps in step, so disposal never reads state.

Tests

test/unit/server/server_delete_live_notifier_test.dart covers both triggers: a watched server deleted through delServer, and a record dropped by a reload (a sync pull). Both failed before this change.

flutter analyze lib test integration_test is clean. test/unit/server is green apart from the pre-existing cases that need /bin/sh and systemd, which a Windows host cannot run.

Fixes #1512.

Summary by CodeRabbit

  • Bug Fixes
    • Server providers now remain readable and disconnected when their underlying server record is deleted or removed during synchronization.
    • Prevented provider errors and unintended state rebuilding during server cleanup.
  • Tests
    • Added coverage for live server providers after direct deletion and synchronization-based removal.

Deleting a server left its `serverProvider` in an error state, and the app
logged `Unhandled (FlutterError): Bad state: Server ... not found` twice per
delete. `ServerNotifier` is `keepAlive`, so `ref.invalidate` does not dispose
it: it runs the dispose listeners and schedules a refresh, and Riverpod
refreshes an *active* element. With the server's editor still on screen — the
page the delete button sits on — `build` ran again after the record was gone
and threw. The same shape sits in the rename and sync-reload paths, which
invalidate a removed id too.

The dispose listener also read `state` to close the SSH client, which forces
that rebuild; the throw came before the `close`, so the connection stayed open.

`build` now keeps the state it last had when the record is gone: its readings,
marked disconnected. Nothing polls it again — `refresh` walks the servers that
still exist — and a restore that reuses the id is picked up by the lookup. The
client is closed from a field that every path installing or dropping one keeps
in step, so disposal no longer reads `state`.

Adds `server_delete_live_notifier_test.dart`: deleting a watched server, and a
record dropped by a sync pull, both leave the provider readable. Both cases
failed before this change.

Refs #1512.
@coderabbitai

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Essentials

Run ID: ad64ab43-359e-4127-8bb2-ecf763496b2f

📥 Commits

Reviewing files that changed from the base of the PR and between 728e702 and 04ea800.

📒 Files selected for processing (2)
  • lib/data/provider/server/single.dart
  • test/unit/server/server_delete_live_notifier_test.dart

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

ServerNotifier now keeps a private client reference for disposal and connection cleanup. When its server record is missing, it preserves prior state as disconnected and clears the client and latency. The change adds widget tests for direct deletion and sync removal while live providers are watched.

Suggested reviewers: lollipopkit

Priority: ➖ Normal

Change: Bug fix · Severity of issue fixed: Medium

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The changes satisfy the deleted-server provider objective in issue #1512. ServerNotifier.build now preserves the previous state as disconnected when the server record is missing. _client is stored… Handle a missing server ID without throwing when the notifier has no previous state, or otherwise prove that this state cannot occur during the required entry lifecycle. Add the required graceful armv8l update-check path and automated cov…
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing a deleted server's notifier from entering an error state.
Out of Scope Changes check ✅ Passed The changes are limited to ServerNotifier lifecycle and disposal handling plus regression tests for deleted servers and sync removal. These changes directly support issue #1512 and do not show unrel…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Full details: Linked Issues check

Explanation

The changes satisfy the deleted-server provider objective in issue #1512. ServerNotifier.build now preserves the previous state as disconnected when the server record is missing. _client is stored separately, so disposal does not read provider state. The added tests cover direct deletion and sync removal. However, build still throws StateError('Server $serverId not found') when no previous state exists. The reviewed changes also contain no update-check handling or test for unsupported Android armv8l. The evidence does not establish the full opening, editing, deleting, and creating lifecycle requirement. The SSH warnings have no specific fix requirement in #1512.

Resolution

Handle a missing server ID without throwing when the notifier has no previous state, or otherwise prove that this state cannot occur during the required entry lifecycle. Add the required graceful armv8l update-check path and automated coverage. Add lifecycle coverage for opening, editing, deleting, and creating entries if existing behavior does not already prove those cases.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from lollipopkit September 19, 2026 14:26
@GT-610
GT-610 merged commit c566706 into main Sep 19, 2026
14 checks passed
@GT-610
GT-610 deleted the fix/server-delete-live-notifier branch September 19, 2026 14:50
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.

..

1 participant