mirror of
https://github.com/anthropics/claude-code.git
synced 2026-04-26 23:56:43 +00:00
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
174 lines
5.6 KiB
Python
174 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Disk space utilities for Claude Code hooks.
|
|
|
|
Provides helper functions to detect and handle disk space issues (ENOSPC errors)
|
|
in a user-friendly manner.
|
|
"""
|
|
|
|
import errno
|
|
import os
|
|
import sys
|
|
from typing import Optional, Tuple
|
|
|
|
|
|
# ENOSPC errno value (28 on Linux/Mac)
|
|
ENOSPC_ERRNO = errno.ENOSPC
|
|
|
|
|
|
def is_disk_space_error(exception: Exception) -> bool:
|
|
"""Check if an exception is related to disk space issues.
|
|
|
|
Args:
|
|
exception: The exception to check
|
|
|
|
Returns:
|
|
True if the exception indicates a disk space issue
|
|
"""
|
|
# Check for OSError with ENOSPC errno
|
|
if isinstance(exception, OSError):
|
|
if hasattr(exception, 'errno') and exception.errno == ENOSPC_ERRNO:
|
|
return True
|
|
# Also check strerror for various disk space error messages
|
|
if hasattr(exception, 'strerror') and exception.strerror:
|
|
strerror_lower = exception.strerror.lower()
|
|
disk_space_indicators = [
|
|
'no space left on device',
|
|
'disk quota exceeded',
|
|
'not enough space',
|
|
'insufficient disk space',
|
|
]
|
|
if any(indicator in strerror_lower for indicator in disk_space_indicators):
|
|
return True
|
|
|
|
# Check error message string as fallback
|
|
error_str = str(exception).lower()
|
|
if 'enospc' in error_str or 'no space left' in error_str:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def get_disk_space_warning() -> str:
|
|
"""Get a user-friendly warning message for disk space issues.
|
|
|
|
Returns:
|
|
Warning message string
|
|
"""
|
|
return (
|
|
"WARNING: Disk space issue detected. Your disk may be full or nearly full.\n"
|
|
"This can cause Claude Code to become unresponsive or crash.\n"
|
|
"\n"
|
|
"Recommended actions:\n"
|
|
" 1. Free up disk space by deleting unnecessary files\n"
|
|
" 2. Check available space with: df -h\n"
|
|
" 3. Clean up temporary files: sudo rm -rf /tmp/* (use with caution)\n"
|
|
" 4. Empty trash/recycle bin\n"
|
|
" 5. Consider removing old Docker images: docker system prune"
|
|
)
|
|
|
|
|
|
def check_available_disk_space(path: str = None, min_bytes: int = 10 * 1024 * 1024) -> Tuple[bool, Optional[str]]:
|
|
"""Check if there's sufficient disk space available.
|
|
|
|
Args:
|
|
path: Path to check (defaults to home directory)
|
|
min_bytes: Minimum required bytes (default: 10MB)
|
|
|
|
Returns:
|
|
Tuple of (has_space, warning_message)
|
|
- has_space: True if sufficient space available
|
|
- warning_message: Warning string if low on space, None otherwise
|
|
"""
|
|
if path is None:
|
|
path = os.path.expanduser("~")
|
|
|
|
try:
|
|
# Get disk usage statistics
|
|
stat = os.statvfs(path)
|
|
available_bytes = stat.f_frsize * stat.f_bavail
|
|
|
|
if available_bytes < min_bytes:
|
|
available_mb = available_bytes / (1024 * 1024)
|
|
required_mb = min_bytes / (1024 * 1024)
|
|
return False, (
|
|
f"Low disk space warning: Only {available_mb:.1f}MB available "
|
|
f"(recommended minimum: {required_mb:.1f}MB)\n"
|
|
f"{get_disk_space_warning()}"
|
|
)
|
|
|
|
return True, None
|
|
|
|
except (OSError, AttributeError):
|
|
# os.statvfs not available on all platforms (e.g., Windows)
|
|
# Return True and let actual write operations fail if there's no space
|
|
return True, None
|
|
|
|
|
|
def safe_write_file(path: str, content: str, warn_on_disk_error: bool = True) -> Tuple[bool, Optional[str]]:
|
|
"""Safely write content to a file with disk space error handling.
|
|
|
|
Args:
|
|
path: Path to write to
|
|
content: Content to write
|
|
warn_on_disk_error: If True, print warning to stderr on disk space errors
|
|
|
|
Returns:
|
|
Tuple of (success, error_message)
|
|
- success: True if write succeeded
|
|
- error_message: Error description if failed, None otherwise
|
|
"""
|
|
try:
|
|
# Ensure directory exists
|
|
dir_path = os.path.dirname(path)
|
|
if dir_path:
|
|
os.makedirs(dir_path, exist_ok=True)
|
|
|
|
with open(path, 'w') as f:
|
|
f.write(content)
|
|
|
|
return True, None
|
|
|
|
except Exception as e:
|
|
if is_disk_space_error(e):
|
|
error_msg = f"Disk space error writing to {path}: {e}\n{get_disk_space_warning()}"
|
|
if warn_on_disk_error:
|
|
print(error_msg, file=sys.stderr)
|
|
return False, error_msg
|
|
else:
|
|
return False, f"Error writing to {path}: {e}"
|
|
|
|
|
|
def safe_append_file(path: str, content: str, warn_on_disk_error: bool = True) -> Tuple[bool, Optional[str]]:
|
|
"""Safely append content to a file with disk space error handling.
|
|
|
|
Args:
|
|
path: Path to append to
|
|
content: Content to append
|
|
warn_on_disk_error: If True, print warning to stderr on disk space errors
|
|
|
|
Returns:
|
|
Tuple of (success, error_message)
|
|
- success: True if append succeeded
|
|
- error_message: Error description if failed, None otherwise
|
|
"""
|
|
try:
|
|
# Ensure directory exists
|
|
dir_path = os.path.dirname(path)
|
|
if dir_path:
|
|
os.makedirs(dir_path, exist_ok=True)
|
|
|
|
with open(path, 'a') as f:
|
|
f.write(content)
|
|
|
|
return True, None
|
|
|
|
except Exception as e:
|
|
if is_disk_space_error(e):
|
|
error_msg = f"Disk space error appending to {path}: {e}\n{get_disk_space_warning()}"
|
|
if warn_on_disk_error:
|
|
print(error_msg, file=sys.stderr)
|
|
return False, error_msg
|
|
else:
|
|
return False, f"Error appending to {path}: {e}"
|