Add disk space error handling utilities for better UX when disk is full

When disk space runs out, Claude Code can become unresponsive or crash without
clear feedback. This adds:

- New disk_space_utils.py module with:
  - ENOSPC error detection (errno 28)
  - User-friendly warning messages with remediation steps
  - Disk space availability checking
  - Safe file write/append helpers

- Updated security_reminder_hook.py to:
  - Check disk space at startup and warn users proactively
  - Detect disk space errors during state file operations
  - Provide actionable guidance when disk issues are detected

The warnings include specific remediation steps (df -h, cleaning /tmp,
emptying trash, docker prune) to help users resolve the issue.

Slack context: https://anthropic.slack.com/archives/C07VBSHV7EV/p1770941952212839

https://claude.ai/code/session_017ywHZBHvZasAWS6qcKXCb3
This commit is contained in:
Claude
2026-02-13 00:29:33 +00:00
parent 232213304d
commit 965dbf27eb
2 changed files with 227 additions and 5 deletions

View File

@@ -10,18 +10,40 @@ import random
import sys
from datetime import datetime
# Import disk space utilities
try:
from disk_space_utils import (
is_disk_space_error,
get_disk_space_warning,
check_available_disk_space,
safe_write_file,
safe_append_file,
)
DISK_UTILS_AVAILABLE = True
except ImportError:
# Fallback if disk_space_utils not available
DISK_UTILS_AVAILABLE = False
# Debug log file
DEBUG_LOG_FILE = "/tmp/security-warnings-log.txt"
# Track if we've already warned about disk space in this session
_disk_space_warned = False
def debug_log(message):
"""Append debug message to log file with timestamp."""
global _disk_space_warned
try:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
with open(DEBUG_LOG_FILE, "a") as f:
f.write(f"[{timestamp}] {message}\n")
except Exception as e:
# Silently ignore logging errors to avoid disrupting the hook
# Check if this is a disk space error and warn the user
if DISK_UTILS_AVAILABLE and is_disk_space_error(e) and not _disk_space_warned:
_disk_space_warned = True
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
# Continue silently to avoid disrupting the hook
pass
@@ -158,26 +180,44 @@ def cleanup_old_state_files():
def load_state(session_id):
"""Load the state of shown warnings from file."""
global _disk_space_warned
state_file = get_state_file(session_id)
if os.path.exists(state_file):
try:
with open(state_file, "r") as f:
return set(json.load(f))
except (json.JSONDecodeError, IOError):
except json.JSONDecodeError:
debug_log(f"JSON decode error reading state file: {state_file}")
return set()
except Exception as e:
# Check for disk-related errors (corrupted filesystem, etc.)
if DISK_UTILS_AVAILABLE and is_disk_space_error(e):
if not _disk_space_warned:
_disk_space_warned = True
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
debug_log(f"Error loading state file: {e}")
return set()
return set()
def save_state(session_id, shown_warnings):
"""Save the state of shown warnings to file."""
global _disk_space_warned
state_file = get_state_file(session_id)
try:
os.makedirs(os.path.dirname(state_file), exist_ok=True)
with open(state_file, "w") as f:
json.dump(list(shown_warnings), f)
except IOError as e:
debug_log(f"Failed to save state file: {e}")
pass # Fail silently if we can't save state
except Exception as e:
# Check for disk space errors and provide user-friendly warning
if DISK_UTILS_AVAILABLE and is_disk_space_error(e):
if not _disk_space_warned:
_disk_space_warned = True
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
debug_log(f"Disk space error saving state file: {e}")
else:
debug_log(f"Failed to save state file: {e}")
# Fail silently to not disrupt operation
def check_patterns(file_path, content):
@@ -216,6 +256,8 @@ def extract_content_from_input(tool_name, tool_input):
def main():
"""Main hook function."""
global _disk_space_warned
# Check if security reminders are enabled
security_reminder_enabled = os.environ.get("ENABLE_SECURITY_REMINDER", "1")
@@ -223,6 +265,13 @@ def main():
if security_reminder_enabled == "0":
sys.exit(0)
# Check for low disk space and warn user (only once per session)
if DISK_UTILS_AVAILABLE and not _disk_space_warned:
has_space, warning = check_available_disk_space()
if not has_space:
_disk_space_warned = True
print(f"[Security Hook] {warning}", file=sys.stderr)
# Periodically clean up old state files (10% chance per run)
if random.random() < 0.1:
cleanup_old_state_files()