A Python service can remain alive with its port in LISTEN and still reset incoming requests if the process has run out of file descriptors. In this reported macOS LaunchAgent incident, a SQLite connection leak was the main cause: the process had used 255 of its 256 soft-limit descriptors. The key mistake was treating with conn: as if it closed a Python sqlite3 connection. It does not.
What happened when the service was still listening?
In a first-person incident report, ACS Developer describes a self-built local web UI with a job queue, running as a macOS LaunchAgent. The process remained running and its port appeared in LISTEN, but HTTP requests were reset. The same symptom over a VPN route led the author to investigate the service process rather than assume the problem was limited to one network path. A listening socket and a live process do not prove that the service still has enough resources to handle new work.
As an Amazon Associate I earn from qualifying purchases.
The author counted 255 open file descriptors against that process’s soft maxfiles limit of 256. The report attributed 122 descriptors to queue.db and 121 to queue.db-wal, with the remaining descriptors associated with other objects such as sockets. These are measurements from that particular environment, not expected SQLite counts or universal macOS limits. Read the incident account from ACS Developer.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWhy did with conn: leave SQLite connections open?
Python’s sqlite3.Connection context manager manages a transaction; it does not manage the connection’s lifetime. On leaving the block, it commits an open transaction if the block succeeds or rolls it back if an exception occurs. It does not close the connection. The Python 3.13 documentation states: “The context manager neither implicitly opens a new transaction nor closes the connection.” Python’s sqlite3 documentation also recommends closing the connection manually or using contextlib.closing() when a closing context manager is needed.
#1 Best Overall
In the incident, a helper opened a database connection and returned it. The caller used with db() as conn:, apparently expecting the block to close that connection like with open(...). Because the connection was not explicitly closed, the long-running service could accumulate open resources as that code path ran.
What does the -wal file mean?
In SQLite write-ahead logging mode, an open database has a separate write-ahead log file, usually named with a -wal suffix. SQLite says the WAL file is usually deleted when the last connection closes, though it can remain in specified circumstances. Its presence explains why the incident’s descriptor listing included queue.db-wal; it does not establish a fixed descriptor-per-connection ratio or explain the reported counts by itself. See SQLite’s official WAL documentation.
Rank #2
How should a Python SQLite connection be closed?
Choose a pattern that makes connection ownership explicit. Transaction handling and connection closure are separate responsibilities: a transaction context can commit or roll back, while another mechanism must close the connection when its owner is finished.
| Approach | Connection cleanup | Transaction behavior | When it fits |
|---|---|---|---|
Explicit close() in finally |
The finally block runs on normal and exceptional exits, so it can close the connection in either case. |
Manage commit or rollback separately, for example with the connection context manager. | Useful when retaining the existing connection interface and making cleanup visible in the caller. |
contextlib.closing() |
Calls the object’s close() method when the closing block exits, including on exceptions. |
It closes the connection but does not replace transaction handling; use a transaction-management approach as needed. | Useful when a closing context manager makes the intended lifetime clearer. |
The incident author reports using explicit transaction handling and a finally block that calls conn.close(). The author also corrected a separate parent-process log-file descriptor lifecycle issue. Those were changes in that service, not required steps for every SQLite application.
How can you check whether a leak is exhausting the service?
Check the process that is actually serving requests, not just a shell or a separate development process. Compare its open descriptors with the limit applied to that process, identify which resource types dominate, then trace their ownership and closure in the code. A high count alone does not identify the leak; the listing can help distinguish database files from sockets, logs, and other resources.
- Identify the service process. Confirm the process ID for the running service or LaunchAgent and inspect that process’s open descriptors.
- Check its effective limit. Inspect the file-descriptor limit applicable to that process. Do not assume the incident’s 256 soft limit applies to another Mac, service, or launch configuration.
- Group the open resources. Look for repeated database paths, WAL files, sockets, logs, or other resources that could be accumulating. Interpret counts in context rather than assuming a fixed SQLite ratio.
- Trace ownership in code. Find where each connection or file is opened, decide which scope owns it, and ensure that scope closes it on both successful and exceptional exits.
- Repeat a controlled workload. Record the descriptor count before and after repeated requests or calls. A count that keeps increasing points to a lifecycle problem worth investigating; a stable count after the workload is evidence to evaluate alongside successful responses and data checks.
What changed after the reported fix?
ACS Developer reports that 300 calls in a test environment produced a descriptor delta of three or less after the changes. In a separate check, 120 service requests left the reported total at 23 before and after. The author also reports that routes returned HTTP 200 after a restart and that stored data remained present. These are the author’s checks, not independently reproduced results or a guarantee for other services.
Quick Recap
Best Value
Rank #4
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minute

