Skip to content

Catch OSError when persisting heartbeat to queue#113

Open
rtsdque wants to merge 2 commits into
ActivityWatch:masterfrom
rtsdque:fix-issue-8
Open

Catch OSError when persisting heartbeat to queue#113
rtsdque wants to merge 2 commits into
ActivityWatch:masterfrom
rtsdque:fix-issue-8

Conversation

@rtsdque

@rtsdque rtsdque commented Jun 16, 2026

Copy link
Copy Markdown

Problem

RequestQueue.add_request() writes heartbeats to a disk-backed queue via persist-queue. If the disk runs out of space, that write raises an uncaught OSError, crashing the program instead of failing gracefully.

Fix

Wrapped the queue write in a try/except OSError, logging a warning instead of crashing. A dropped heartbeat is an acceptable tradeoff compared to the whole process going down.

Testing

  • Added test_add_request_disk_full, which simulates a disk-full scenario by making persistqueue.put raise OSError, and confirms add_request handles it without crashing.
  • Ran the full existing test suite (pytest tests/test_requestqueue.py), all passing.
  • Ran ruff check . and mypy, no new issues introduced.

Fixes #8

Prevents a crash when the disk is full by catching OSError in add_request() and logging a warning instead of letting it propagate.

Fixes ActivityWatch#8
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds graceful error handling to RequestQueue.add_request so that an OSError (e.g. disk full) from persistqueue.put is logged as a warning rather than propagating and crashing the process. A matching unit test monkey-patches the queue write to raise OSError and verifies no exception escapes.

  • aw_client/client.py: Wraps _persistqueue.put in try/except OSError; the error is logged at WARNING level and the heartbeat is silently dropped.
  • tests/test_requestqueue.py: Adds test_add_request_disk_full covering the new error path; both changed files are missing a trailing newline at EOF.

Confidence Score: 4/5

Safe to merge — the core fix is a small, well-scoped try/except with a companion test; only style nits remain.

The logic change is minimal and correct: an uncaught OSError from a disk-full scenario now becomes a logged warning. The only issues are missing EOF newlines in both changed files, introduced as a side-effect of the patch.

Both aw_client/client.py and tests/test_requestqueue.py are missing a trailing newline at end of file.

Important Files Changed

Filename Overview
aw_client/client.py Wraps _persistqueue.put in a try/except OSError to gracefully handle disk-full errors; loses trailing newline at EOF as a side-effect of the patch.
tests/test_requestqueue.py Adds test_add_request_disk_full that monkey-patches _persistqueue.put to raise OSError and confirms no exception propagates; also missing EOF newline.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[add_request called] --> B{assert endpoint contains /heartbeat}
    B --> C{assert data is dict}
    C --> D[_persistqueue.put QueuedRequest]
    D -->|success| E[Heartbeat queued on disk]
    D -->|OSError| F[logger.warning: Failed to queue request]
    F --> G[Heartbeat dropped silently]
    E --> H[Worker thread dispatches to server]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[add_request called] --> B{assert endpoint contains /heartbeat}
    B --> C{assert data is dict}
    C --> D[_persistqueue.put QueuedRequest]
    D -->|success| E[Heartbeat queued on disk]
    D -->|OSError| F[logger.warning: Failed to queue request]
    F --> G[Heartbeat dropped silently]
    E --> H[Worker thread dispatches to server]
Loading

Reviews (1): Last reviewed commit: "Catch OSError when persisting heartbeat ..." | Re-trigger Greptile

Comment thread aw_client/client.py Outdated
Comment on lines +559 to +560
def register_bucket(self, bucket_id: str, event_type: str) -> None:
self._registered_buckets.append(Bucket(bucket_id, event_type))
self._registered_buckets.append(Bucket(bucket_id, event_type)) No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Both changed files are missing a trailing newline at the end of file (\ No newline at end of file in the diff). The register_bucket line in client.py lost its trailing newline as a side effect of this patch. POSIX requires text files to end with a newline, and many linters/diff tools will flag or silently alter files without one.

Suggested change
def register_bucket(self, bucket_id: str, event_type: str) -> None:
self._registered_buckets.append(Bucket(bucket_id, event_type))
self._registered_buckets.append(Bucket(bucket_id, event_type))
def register_bucket(self, bucket_id: str, event_type: str) -> None:
self._registered_buckets.append(Bucket(bucket_id, event_type))

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread tests/test_requestqueue.py Outdated
Comment on lines +74 to +75
# Should not raise, the OSError should be caught internally and logged instead
rq.add_request("/api/0/buckets/test/heartbeat", {}) No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The new test function is also missing a trailing newline at end of file.

Suggested change
# Should not raise, the OSError should be caught internally and logged instead
rq.add_request("/api/0/buckets/test/heartbeat", {})
# Should not raise, the OSError should be caught internally and logged instead
rq.add_request("/api/0/buckets/test/heartbeat", {})

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

Crashes if no space left on device

1 participant