home-assistant/core · error · InvalidBackupFilename
Refusing to write outside {self._backup_dir}: {candidate}
Error message
Refusing to write outside {self._backup_dir}: {candidate} What it means
Raised by LocalBackupAgent.get_new_backup_path when the candidate path built from suggested_filename(backup) would not land directly inside the configured backup directory (candidate.parent != self._backup_dir). Because suggested_filename does not strip path separators, a crafted backup name containing '/' or '..' could otherwise escape the backup directory; this guard blocks path traversal.
Source
Thrown at homeassistant/components/backup/backup.py:141
@override
def get_backup_path(self, backup_id: str) -> Path:
"""Return the local path to an existing backup.
Raises BackupAgentError if the backup does not exist.
"""
try:
return self._backups[backup_id][1]
except KeyError as err:
raise BackupNotFound(f"Backup {backup_id} does not exist") from err
@override
def get_new_backup_path(self, backup: AgentBackup) -> Path:
"""Return the local path to a new backup."""
candidate = self._backup_dir / suggested_filename(backup)
# suggested_filename does not strip separators; refuse paths that would
# land outside the backup directory.
if candidate.parent != self._backup_dir:
raise InvalidBackupFilename(
f"Refusing to write outside {self._backup_dir}: {candidate}"
)
return candidate
@override
async def async_delete_backup(self, backup_id: str, **kwargs: Any) -> None:
"""Delete a backup file."""
if not self._loaded_backups:
await self._load_backups()
backup_path = self.get_backup_path(backup_id)
await self._hass.async_add_executor_job(backup_path.unlink, True)
LOGGER.debug("Deleted backup located at %s", backup_path)
self._backups.pop(backup_id)
View on GitHub (pinned to 58a3fdb3ea)
Solutions
- Sanitize the backup name before creating it: strip '/', '\\', '..' and other path characters from name and slugify it.
- If a filename with directories is genuinely needed, place files inside the backup dir manually rather than relying on the agent.
- Verify the backup's name field, not just its ID, when this error appears during receive/create flows.
Example fix
# before await client.async_create_backup(name="../../etc/pwned") # after import re safe_name = re.sub(r"[^\w\-. ]", "_", "../../etc/pwned") # '.._.._etc_pwned' await client.async_create_backup(name=safe_name)
Defensive patterns
Strategy: validation
Validate before calling
import re
def safe_backup_name(name: str) -> str:
return re.sub(r"[^\w\-. ]", "_", name).strip(" .") or "backup"
candidate = backup_dir / safe_backup_name(name)
assert candidate.parent == backup_dir Type guard
from pathlib import Path
def is_safe_backup_name(name: str, backup_dir: Path) -> bool:
return (backup_dir / name).parent == backup_dir and name not in ("", ".", "..") Try / catch
from homeassistant.components.backup.manager import InvalidBackupFilename
try:
path = local_agent.get_new_backup_path(backup)
except InvalidBackupFilename as err:
backup.name = safe_backup_name(backup.name)
path = local_agent.get_new_backup_path(backup) Prevention
- Slugify user-supplied backup names before creating backups.
- Never build backup names from file paths or external input verbatim.
- Assert candidate.parent == backup_dir client-side as a cheap invariant.
When it happens
Trigger: Creating/uploading a backup whose name (fed through suggested_filename) contains a path separator — e.g., name="../../etc/evil" or "nested/dir/backup" — when the local agent computes the write path via get_new_backup_path. Also triggered by a legitimately odd name such as a trailing '/' or one that resolves differently after Path normalization.
Common situations: API clients or scripts creating backups with arbitrary, unsanitized names; malicious upload attempts against the backup upload endpoint; backups created on another system whose names contain slashes.
Related errors
- Invalid filename: {suggested_filename}
- At least one available backup agent must be selected, got {a
- Cannot include all addons and specify specific addons
- Invalid agent selected: {agent_id}
- Addons and folders are not supported by core backup
AI-assisted analysis of home-assistant/core@58a3fdb3ea (2026-08-14).
Data as JSON: /api/errors/8a67a2e0ccff8483.
Report an issue: GitHub.