MuntashirAkon/AppManager · error · IOException
Backup is in read-only mode.
Error message
Backup is in read-only mode.
What it means
writeMetadata refuses to write when the BackupItem is not in backup (writable) mode, throwing IOException "Backup is in read-only mode." This protects restored/loaded backup views from being mutated accidentally.
Solutions
- Ensure the BackupItem is created/opened in backup mode (set the backup mode flag before writing).
- Write metadata only during an active backup/creation session, not when viewing or restoring an existing backup.
- If you must edit an existing backup, re-create it via a new backup operation instead of mutating read-only state.
Example fix
// before
MetadataManager.writeMetadata(metadata, backupItem);
// after
if (!backupItem.isBackupMode()) {
throw new IllegalStateException("Cannot write metadata: backup opened in read-only mode");
}
MetadataManager.writeMetadata(metadata, backupItem); Defensive patterns
Strategy: type-guard
Type guard
boolean canWriteMetadata(BackupItems.BackupItem item) { return item.isBackupMode(); } Try / catch
try {
MetadataManager.writeMetadata(metadata, backupFile);
} catch (IOException e) {
if ("Backup is in read-only mode.".equals(e.getMessage())) {
// reopen the BackupItem in backup mode or inform the user
} else throw e;
} Prevention
- Only write metadata within an active backup/creation session.
- Check isBackupMode() before any metadata mutation.
- Keep read-only viewers strictly read-only; re-create backups to modify them.
When it happens
Trigger: Calling writeMetadata with a BackupItem opened in restore/view mode (isBackupMode() == false), e.g. after loading an existing backup for reading rather than creating one during a backup operation.
Common situations: Trying to update metadata of an existing backup from a read-only viewer context, or reusing a BackupItem constructed for restore inside a metadata-editing flow.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- APK files backup is requested but no APK files have been…
- Backup failed for
- Backup failed for
- Could not backup data
- Could not backup KeyStore item.
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/6c0d69fb582b0366.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/backup/MetadataManager.java:107
}
try {
jsonObject = new JSONObject(metadataString);
if (!v5AndUp) {
// Meta is a subset of meta_v2.am.json except for backup_name
jsonObject.put("backup_name", backupItem.getBackupName());
}
BackupMetadataV5.Metadata metadata = new BackupMetadataV5.Metadata(jsonObject);
return new BackupMetadataV5(backupInfo, metadata);
} catch (JSONException e) {
throw new IOException(e.getMessage() + " for path " + metadataFile, e);
}
}
@WorkerThread
@NonNull
public static Map<String, String> writeMetadata(@NonNull BackupMetadataV5 metadata, @NonNull BackupItems.BackupItem backupFile) throws IOException {
if (!backupFile.isBackupMode()) {
throw new IOException("Backup is in read-only mode.");
}
if (metadata.info.version >= 5) {
// v5 and up
return writeMetadataV5(metadata, backupFile);
} else {
// Old style backup
return writeMetadataV2(metadata, backupFile);
}
}
@WorkerThread
@NonNull
private static Map<String, String> writeMetadataV2(@NonNull BackupMetadataV5 metadata, @NonNull BackupItems.BackupItem backupFile) throws IOException {
Path metadataFile = backupFile.getMetadataV2File();
try (OutputStream outputStream = metadataFile.openOutputStream()) {
JSONObject metadataObject = metadata.info.serializeToJson();
JSONUtils.putAll(metadataObject, metadata.metadata.serializeToJson());
// Info is a subset of meta_v2.am.json except for backup_nameView on GitHub (pinned to 0152f468fc)