Summary
On Windows with Python 3.12.x, Path.is_file() raises PermissionError (WinError 5: Access is denied) instead of returning False when called on paths inside directories that have restricted NTFS ACLs. This causes export_custom_agents() to crash with an unhandled exception, and the POST /api/agents/export endpoint returns HTTP 500 to the UI, which shows an error banner.
Affected file: src/gaia/installer/export_import.py
Affected function: _is_custom_agent_dir()
Root Cause
# BEFORE (buggy)
def _is_custom_agent_dir(path: Path) -> bool:
return path.is_dir() and (
(path / "agent.py").is_file() or (path / "agent.yaml").is_file()
)
pathlib.Path.is_file() is documented to return False on errors, but on Windows with Python 3.12.12 it raises PermissionError for NTFS paths where the caller lacks read permission. This is a known CPython regression/platform inconsistency. Any subdirectory of ~/.gaia/agents/ with restricted permissions (e.g. a symlink or directory created by another process with locked-down ACLs) triggers the crash.
Reproduction Steps
- Install GAIA v0.17.2 on Windows 11 with Python 3.12.x
- Create a directory with restricted NTFS permissions inside
~/.gaia/agents/:
$dir = "$env:USERPROFILE\.gaia\agents\restricted-dir"
New-Item -ItemType Directory -Path $dir
$acl = Get-Acl $dir
$acl.SetAccessRuleProtection($true, $false)
Set-Acl -Path $dir -AclObject $acl
- Open the GAIA Agent UI desktop app
- Go to Settings → Custom Agents → Export All
- Click through the security confirmation dialog
- Choose a save path and click Save
Expected: Export succeeds, skipping or ignoring the restricted directory
Actual: PermissionError: [WinError 5] Access is denied is raised; UI shows an error banner; no zip file is produced
Fix
Wrap the file checks in a try/except OSError block:
# AFTER (fixed)
def _is_custom_agent_dir(path: Path) -> bool:
"""A directory qualifies as a custom agent if it holds agent.py or agent.yaml."""
try:
return path.is_dir() and (
(path / "agent.py").is_file() or (path / "agent.yaml").is_file()
)
except OSError:
return False
This matches the documented intent of is_file() (return False on errors) and silently skips any unreadable subdirectory rather than crashing the entire export.
Acceptance Criteria
Environment
|
|
| OS |
Windows 11 |
| Python |
3.12.12 |
| GAIA version |
0.17.2 |
| Hardware |
AMD Ryzen AI MAX+ 395 |
References
Summary
On Windows with Python 3.12.x,
Path.is_file()raisesPermissionError(WinError 5: Access is denied) instead of returningFalsewhen called on paths inside directories that have restricted NTFS ACLs. This causesexport_custom_agents()to crash with an unhandled exception, and thePOST /api/agents/exportendpoint returns HTTP 500 to the UI, which shows an error banner.Affected file:
src/gaia/installer/export_import.pyAffected function:
_is_custom_agent_dir()Root Cause
pathlib.Path.is_file()is documented to returnFalseon errors, but on Windows with Python 3.12.12 it raisesPermissionErrorfor NTFS paths where the caller lacks read permission. This is a known CPython regression/platform inconsistency. Any subdirectory of~/.gaia/agents/with restricted permissions (e.g. a symlink or directory created by another process with locked-down ACLs) triggers the crash.Reproduction Steps
~/.gaia/agents/:Expected: Export succeeds, skipping or ignoring the restricted directory
Actual:
PermissionError: [WinError 5] Access is deniedis raised; UI shows an error banner; no zip file is producedFix
Wrap the file checks in a
try/except OSErrorblock:This matches the documented intent of
is_file()(returnFalseon errors) and silently skips any unreadable subdirectory rather than crashing the entire export.Acceptance Criteria
_is_custom_agent_dir()does not raise when called on a path inside~/.gaia/agents/that has restricted NTFS permissions~/.gaia/agents/contains a mix of valid agent directories and unreadable/restricted subdirectoriesbundle.jsonagent_idsPath.is_file()to raisePermissionError; assert_is_custom_agent_dir()returnsFalserather than propagating the exceptionEnvironment
References