Skip to content

fix(plugins): quarantine stuck plugins without deleting them#2160

Open
bajrangCoder wants to merge 6 commits into
Acode-Foundation:mainfrom
bajrangCoder:fix/plugin-timeout-quarantine
Open

fix(plugins): quarantine stuck plugins without deleting them#2160
bajrangCoder wants to merge 6 commits into
Acode-Foundation:mainfrom
bajrangCoder:fix/plugin-timeout-quarantine

Conversation

@bajrangCoder
Copy link
Copy Markdown
Member

@bajrangCoder bajrangCoder commented Jun 6, 2026

Summary

Fixes plugin disappearance caused by startup load failures/timeouts deleting installed plugin folders.

New Behavior

  • Plugin files are no longer deleted when plugin loading fails.
  • A plugin that throws a real load error is marked broken and persisted as disabled via pluginsDisabled.
  • A plugin that exceeds the normal load timeout is treated as slow first:
    • after 15s, app startup continues
    • the plugin continues loading in the background
    • if it finishes successfully, it is marked loaded and the timeout state is cleared
  • If a timed-out plugin still has not finished after 60s, it is treated as stuck and persisted as disabled.
  • Disabled/quarantined plugins will not be retried on every app restart, but users still keep the installed plugin files and can manually re-enable or uninstall them.

Why

Previously, the startup plugin loader treated slow plugins and broken plugins the same. Any failed/timed-out plugin was added to failedPlugins, then cleanupFailedPlugins() deleted its directory from PLUGIN_DIR.

That could make plugins appear to disappear permanently, especially on slower devices where valid plugins may exceed the startup timeout.

Fixes: #2010

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 6, 2026

Greptile Summary

This PR replaces the delete-on-failure plugin loader with a two-tier quarantine system: plugins that exceed 15 s during startup continue loading in the background, and are only persisted as disabled (without deleting files) if they are still unresolved after 60 s total. Genuine load errors are immediately marked broken and written to pluginsDisabled.

  • loadPlugins.js: Introduces PluginLoadTimeoutError, loadPluginWithTimeout, a serialized pluginDisabledUpdateQueue, and the markPluginLoaded/markPluginBroken/markPluginTimedOut helpers. Fixes the loadOnlyTheme filter to also respect pluginsDisabled, closing the loop where quarantined theme plugins were retried on every restart.
  • installPlugin.js: Swaps the bare loadPlugin import for the new exported loadPluginWithTimeout, passing justInstalled = true so any eventual background recovery also clears the disabled entry.

Confidence Score: 5/5

Safe to merge — plugin files are preserved on failure, the quarantine logic is well-serialized, and the theme-plugin retry loop is now correctly gated on the persisted disabled map.

All three issues raised in earlier review rounds (string-match ambiguity, settings write ordering, and theme plugins retried on restart) are addressed in this revision. The only remaining finding is a no-op settings write in updatePluginDisabled when clearing a disabled entry that was never set, which is harmless and easy to fix.

No files require special attention; loadPlugins.js carries all the new complexity but the logic is clearly structured.

Important Files Changed

Filename Overview
src/lib/loadPlugins.js Core rewrite of plugin loading: replaces the deletion-on-failure pattern with a two-tier timeout system (15 s to continue startup, 60 s to quarantine). Introduces PluginLoadTimeoutError, a serialized pluginDisabledUpdateQueue, and exports loadPluginWithTimeout. Correctly fixes the loadOnlyTheme filter to respect pluginsDisabled. One minor issue: updatePluginDisabled skips its no-op guard when disabled=false and the key is absent, producing a redundant settings.update.
src/lib/installPlugin.js Import swapped from the standalone loadPlugin to the new exported loadPluginWithTimeout, passing justInstalled=true. No other logic changes; return value is correctly unused (install state is saved regardless of load outcome).

Sequence Diagram

sequenceDiagram
    participant App as App Startup
    participant LPT as loadPluginWithTimeout
    participant LP as loadPlugin (background)
    participant Settings as settings (pluginsDisabled)

    App->>LPT: loadPluginWithTimeout(pluginId)
    LPT->>LP: loadPlugin(pluginId) [background]
    Note over LPT: 15 s timeout races loadPlugin

    alt Plugin loads within 15 s
        LP-->>LPT: resolved
        LPT->>Settings: markPluginLoaded (clear disabled if set)
        LPT-->>App: return true
    else Plugin times out at 15 s
        LPT->>LPT: markPluginTimedOut (BROKEN_PLUGINS in-memory)
        LPT-->>App: return false (startup continues)
        Note over LP: still running in background
        alt Plugin loads before 60 s
            LP-->>LPT: resolved
            LPT->>Settings: markPluginLoaded / updatePluginDisabled(false)
        else Plugin still stuck at 60 s
            LPT->>Settings: markPluginBroken / updatePluginDisabled(true)
        end
    else Plugin throws real error
        LP-->>LPT: rejected
        LPT->>Settings: markPluginBroken / updatePluginDisabled(true)
        LPT-->>App: throw error
    end
Loading

Reviews (5): Last reviewed commit: "fix" | Re-trigger Greptile

Comment thread src/lib/loadPlugins.js
Comment thread src/lib/loadPlugins.js
@bajrangCoder

This comment was marked as outdated.

@bajrangCoder

This comment was marked as outdated.

@UnschooledGamer UnschooledGamer added the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jun 6, 2026
@github-actions github-actions Bot removed the CI: RUN ON-DEMAND PREVIEW RELEASES Triggers an on-demand preview build for this pull request via CI workflow. label Jun 6, 2026
@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 6, 2026

Preview Release for this, has been built.

Click here to view that github actions build

@bajrangCoder

This comment was marked as outdated.

@bajrangCoder

This comment was marked as outdated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Plugins sometimes disappear

2 participants