{"record":{"id":"9e9ee9b4d915618c","repo":"beemdevelopment/Aegis","slug":"backup-file-already-exists","errorCode":null,"errorMessage":"Backup file already exists","messagePattern":"Backup file already exists","errorType":"exception","errorClass":"VaultRepositoryException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/vault/VaultBackupManager.java","lineNumber":126,"sourceCode":"    }\n\n    private void createBackup(File tempFile, Uri dirUri, int versionsToKeep)\n            throws VaultRepositoryException, VaultBackupPermissionException {\n        FileInfo fileInfo = new FileInfo(FILENAME_PREFIX);\n        DocumentFile dir = DocumentFile.fromTreeUri(_context, dirUri);\n\n        try {\n            Log.i(TAG, String.format(\"Creating backup at %s: %s\", Uri.decode(dir.getUri().toString()), fileInfo.toString()));\n\n            if (!hasPermissionsAt(dirUri)) {\n                throw new VaultBackupPermissionException(\"No persisted URI permissions\");\n            }\n\n            // If we create a file with a name that already exists, SAF will append a number\n            // to the filename and write to that instead. We can't overwrite existing files, so\n            // just avoid that altogether by checking beforehand.\n            if (dir.findFile(fileInfo.toString()) != null) {\n                throw new VaultRepositoryException(\"Backup file already exists\");\n            }\n\n            DocumentFile file = dir.createFile(\"application/json\", fileInfo.toString());\n            if (file == null) {\n                throw new VaultRepositoryException(\"createFile returned null\");\n            }\n\n            try (FileInputStream inStream = new FileInputStream(tempFile);\n                 OutputStream outStream = _context.getContentResolver().openOutputStream(file.getUri())) {\n                if (outStream == null) {\n                    throw new IOException(\"openOutputStream returned null\");\n                }\n                IOUtils.copy(inStream, outStream);\n            } catch (IOException e) {\n                throw new VaultRepositoryException(e);\n            }\n        } catch (VaultRepositoryException | VaultBackupPermissionException e) {\n            Log.e(TAG, String.format(\"Unable to create backup: %s\", e.toString()));","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/vault/VaultBackupManager.java#L108-L144","documentation":"createBackup refuses to start a backup when a file with the target name (FileInfo.toString(), normally the date-based name) already exists in the SAF directory. Because Storage Access Framework's createFile cannot overwrite and would silently create 'name (1).json' instead, the library throws VaultRepositoryException('Backup file already exists') to avoid duplicated or misleading backup files.","triggerScenarios":"createBackup runs with a FileInfo whose filename matches an existing entry in dir (dir.findFile(...) != null); typically two backups scheduled with the same date bucket, a restored/copy-pasted backup file, or a user-picked folder that already contains Aegis exports.","commonSituations":"Running backup twice on the same day with a date-precision filename; a device clock misconfiguration reusing a previous backup name; user manually copies old backups into the chosen backup folder; restoring an app backup that includes the target directory.","solutions":["Delete the existing file first: dir.findFile(name).delete() (requires write grant on the tree), or pick a new filename that includes a timestamp with seconds","Check existence before calling scheduleBackup and skip/log instead of throwing when the same-day backup is intentional","Change the backup FileInfo to include a unique component (e.g. HHmmss or a UUID suffix) so collisions cannot recur","Choose a different backup directory or move the stale file out"],"exampleFix":"// before\nDocumentFile existing = dir.findFile(fileInfo.toString());\nif (existing != null) { /* VaultRepositoryException: Backup file already exists */ }\n// after\nDocumentFile existing = dir.findFile(fileInfo.toString());\nif (existing != null) {\n    existing.delete(); // overwrite policy: replace stale backup\n}\nDocumentFile file = dir.createFile(\"application/json\", fileInfo.toString());","handlingStrategy":"validation","validationCode":"DocumentFile dir = DocumentFile.fromTreeUri(context, dirUri);\nString name = fileInfo.toString();\nif (dir.findFile(name) != null) {\n    dir.findFile(name).delete(); // or generate a new unique name\n}","typeGuard":"boolean fileExistsInDir(DocumentFile dir, String name) {\n    return dir != null && name != null && dir.findFile(name) != null;\n}","tryCatchPattern":"try {\n    backupManager.scheduleBackup(dirUri, fileInfo);\n} catch (VaultRepositoryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"already exists\")) {\n        backupManager.scheduleBackup(dirUri, fileInfo.withTimestampSeconds());\n    }\n}","preventionTips":["Include a timestamp with sub-day precision (or UUID) in backup filenames","Decide an overwrite policy up front (delete-then-create or skip)","Check dir.findFile before every backup run"],"tags":["android","saf","backup","file-exists"],"backgroundTag":"file-already-exists","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}