{"record":{"id":"935a528234572ef9","repo":"apache/beam","slug":"err-re-raised-oserror-during-delete","errorCode":null,"errorMessage":"err (re-raised OSError during delete)","messagePattern":"err \\(re-raised OSError during delete\\)","errorType":"exception","errorClass":"IOError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/localfilesystem.py","lineNumber":347,"sourceCode":"    \"\"\"Deletes files or directories at the provided paths.\n    Directories will be deleted recursively.\n\n    Args:\n      paths: list of paths that give the file objects to be deleted\n\n    Raises:\n      ``BeamIOError``: if any of the delete operations fail\n    \"\"\"\n    def _delete_path(path):\n      \"\"\"Recursively delete the file or directory at the provided path.\n      \"\"\"\n      try:\n        if os.path.isdir(path):\n          shutil.rmtree(path)\n        else:\n          os.remove(path)\n      except OSError as err:\n        raise IOError(err)\n\n    exceptions = {}\n\n    def try_delete(path):\n      try:\n        _delete_path(path)\n      except Exception as e:  # pylint: disable=broad-except\n        exceptions[path] = e\n\n    for match_result in self.match(paths):\n      metadata_list = match_result.metadata_list\n\n      if not metadata_list:\n        exceptions[match_result.pattern] = \\\n          IOError('No files found to delete under: %s' % match_result.pattern)\n\n      for metadata in match_result.metadata_list:\n        try_delete(metadata.path)","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/localfilesystem.py#L329-L365","documentation":"Inside _delete_path, when os.remove/shutil.rmtree raises OSError, the code re-raises it as IOError(err). This can be raised per-path during delete(); in the current code try_delete catches it, but this specific raise at line 347 surfaces when the underlying OS delete fails (permission denied, file locked, etc.).","triggerScenarios":"Calling delete()/try_delete() on a path where the file is open by another process, the filesystem is read-only, permissions deny removal, or the file disappears between the isdir check and the remove call (TOCTOU race).","commonSituations":"Deleting temp files on shared/NFS mounts with permission issues; cleanup jobs racing with a running writer process; removing files owned by another user; Windows file-locking.","solutions":["Check and fix file/directory permissions (chmod/chown) so the process can delete the target.","Ensure no other process holds the file open before deleting.","Retry the delete after a short delay if the failure is transient (e.g. NFS staleness).","Catch the error per-path and record it; delete() already collects per-path exceptions and reports them together."],"exampleFix":"// before\nfs.delete(['/tmp/locked.txt'])\n// after\nimport time\nfor attempt in range(3):\n    try:\n        fs.delete(['/tmp/locked.txt'])\n        break\n    except IOError as e:\n        time.sleep(1)  # retry transient deletion failure","handlingStrategy":"try-catch","validationCode":"import os\nassert os.access(os.path.dirname(path) or '.', os.W_OK), 'cannot delete in dir'","typeGuard":null,"tryCatchPattern":"try:\n    fs.delete([path])\nexcept BeamIOError as e:\n    log.warning('delete failed for %s: %s', path, e)","preventionTips":["Check write/execute permissions on the parent directory.","Ensure no process holds the file open during cleanup.","Retry transient deletion failures with backoff."],"tags":["filesystem","oserror","file-deletion"],"backgroundTag":"file-write-failed","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}