Skip to content

fix(desktop): cleanup stale packaged backend processes on startup#1479

Open
futuremeng wants to merge 2 commits intoagentscope-ai:mainfrom
futuremeng:feat/upstream/desktop-stale-cleanup
Open

fix(desktop): cleanup stale packaged backend processes on startup#1479
futuremeng wants to merge 2 commits intoagentscope-ai:mainfrom
futuremeng:feat/upstream/desktop-stale-cleanup

Conversation

@futuremeng
Copy link
Contributor

When a packaged desktop app exits abnormally, the backend (uvicorn) process
may survive as an orphan, causing port conflicts on the next launch.

This commit adds a lightweight cleanup step at the start of the desktop
command: it scans running processes for the packaged backend signature
(.app/Contents/Resources/env/bin/python ... uvicorn copaw.app._app:app)
and terminates any stale instances with SIGTERM, waiting up to 2 s for
graceful shutdown before escalating to SIGKILL.

Source-code dev processes (e.g. python -m copaw app --reload) are excluded
from the match pattern and are never affected.

Description

The packaged desktop app launches a backend server subprocess. If the app
crashes or is force-killed, the subprocess may remain alive. On the next
launch, the port-scan logic finds a free port successfully, but the orphan
process can interfere with shared resources. This PR adds a safe pre-launch
cleanup that only targets clearly-identified packaged backends.

Related Issue: N/A

Security Considerations: The cleanup uses os.kill with SIGTERM/SIGKILL
scoped strictly to processes matching the .app-bundled backend path pattern.
No shell injection is involved (args are passed as a list to subprocess.check_output).

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Refactoring

Component(s) Affected

  • Core / Backend (app, agents, config, providers, utils, local_models)
  • Console (frontend web UI)
  • Channels (DingTalk, Feishu, QQ, Discord, iMessage, etc.)
  • Skills
  • CLI
  • Documentation (website)
  • Tests
  • CI/CD
  • Scripts / Deploy

Checklist

  • I ran pre-commit run --all-files locally and it passes
  • If pre-commit auto-fixed files, I committed those changes and reran checks
  • I ran tests locally (pytest or as relevant) and they pass
  • Documentation updated (if needed)
  • Ready for review

Testing

  1. Start CoPaw desktop app, then force-kill the process (e.g. kill -9 <pid>).
  2. Launch the desktop app again — previously the orphan backend would conflict.
  3. With this fix, the stale process is detected and terminated before the new
    backend starts. Verify via lsof -i :<port> that only one backend is listening.

Local Verification Evidence

pytest tests/unit/providers/
# 14 passed

When a packaged desktop app exits abnormally, the backend (uvicorn) process
may survive as an orphan, causing port conflicts on the next launch.

This commit adds a lightweight cleanup step at the start of the desktop
command: it scans running processes for the packaged backend signature
(.app/Contents/Resources/env/bin/python ... uvicorn copaw.app._app:app)
and terminates any stale instances with SIGTERM, waiting up to 2 s for
graceful shutdown before escalating to SIGKILL.

Source-code dev processes (e.g. python -m copaw app --reload) are excluded
from the match pattern and are never affected.
Copilot AI review requested due to automatic review settings March 14, 2026 06:51
@github-actions github-actions bot added the first-time-contributor PR created by a first time contributor label Mar 14, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a crucial pre-launch cleanup step for the desktop application. It addresses an issue where abnormally terminated desktop apps could leave behind orphaned backend processes, leading to port conflicts and instability on subsequent launches. By proactively identifying and terminating these specific stale processes, the change ensures a clean and reliable startup experience for the desktop application.

Highlights

  • Stale Process Cleanup: Implemented a mechanism to detect and terminate orphaned backend (uvicorn) processes of the packaged desktop application on startup, preventing port conflicts and resource interference.
  • Targeted Cleanup Logic: The cleanup specifically targets processes matching the signature of a packaged desktop backend ('.app/Contents/Resources/env/bin/python ... uvicorn copaw.app._app:app'), ensuring development or source-code processes are unaffected.
  • Graceful and Forceful Termination: Stale processes are first sent a SIGTERM for graceful shutdown, with a 2-second grace period, followed by a SIGKILL if they persist.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/copaw/cli/desktop_cmd.py
    • Added functions to detect and terminate stale desktop backend processes.
    • Integrated the cleanup logic into the desktop command startup sequence.
Activity
  • The author, futuremeng, has implemented a solution to address potential port conflicts caused by orphaned backend processes.
  • Local testing was performed, including force-killing the app and verifying cleanup, with pytest passing.
  • The pull request is marked as ready for review.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Generative AI Prohibited Use Policy, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a helpful cleanup mechanism for stale backend processes that might be left running after an abnormal application exit. The approach of identifying and terminating these processes on startup is sound. My review focuses on improving the robustness of the implementation by replacing broad exception handlers (except Exception) with more specific ones. This will make the code more maintainable and prevent potential bugs from being hidden.

Comment on lines +110 to +111
except Exception:
return []
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The except Exception: is too broad and can hide unexpected errors. It's better to catch specific exceptions that subprocess.check_output is known to raise, such as subprocess.CalledProcessError and FileNotFoundError. Also, consider logging the error for easier debugging, which will be helpful if process scanning fails for some reason.

Suggested change
except Exception:
return []
except (subprocess.CalledProcessError, FileNotFoundError) as e:
_log_desktop(f'[desktop] Failed to scan processes while cleaning up: {e}')
return []

Comment on lines +142 to +145
except ProcessLookupError:
continue
except Exception:
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using except Exception: is too broad. It's better to catch specific exceptions. In this case, besides ProcessLookupError, os.kill might raise a PermissionError if the current user doesn't have the rights to terminate the process. It's good practice to handle these specific cases and let other unexpected exceptions propagate. You can combine the except blocks for cleaner code.

Suggested change
except ProcessLookupError:
continue
except Exception:
continue
except (ProcessLookupError, PermissionError):
continue

Comment on lines +160 to +161
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The except Exception: is too broad and silently ignores any error that occurs when trying to SIGKILL a process. It's better to catch specific exceptions like ProcessLookupError (if the process died just before the kill attempt) or PermissionError.

Suggested change
except Exception:
pass
except (ProcessLookupError, PermissionError):
pass

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a startup cleanup step to the desktop CLI command to prevent port/resource conflicts caused by orphaned backend (uvicorn/FastAPI) processes that can survive abnormal desktop-app exits.

Changes:

  • Introduces process scanning via ps to find packaged-desktop backend candidates.
  • Sends SIGTERM, waits briefly, then escalates to SIGKILL for surviving processes.
  • Logs the list of cleaned PIDs before starting the new backend.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +130 to +133
if (
".app/Contents/Resources/env/bin/python" in cmd
and "-m uvicorn" in cmd
and "copaw.app._app:app" in cmd
Comment on lines +129 to +135
# Only clean stale desktop-packaged backend processes.
if (
".app/Contents/Resources/env/bin/python" in cmd
and "-m uvicorn" in cmd
and "copaw.app._app:app" in cmd
):
stale_pids.append(pid)
@futuremeng futuremeng requested a deployment to maintainer-approved March 14, 2026 15:17 — with GitHub Actions Waiting
@xieyxclack xieyxclack requested a review from rayrayraykk March 16, 2026 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

first-time-contributor PR created by a first time contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants