{"record":{"id":"d0d2d67f7adcf768","repo":"MuntashirAkon/AppManager","slug":"relativedir-not-set","errorCode":null,"errorMessage":"relativeDir not set.","messagePattern":"relativeDir not set\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/db/entity/Backup.java","lineNumber":92,"sourceCode":"\n    @ColumnInfo(name = \"installer_app\")\n    @Nullable\n    public String installer;\n\n    @ColumnInfo(name = \"info_hash\")\n    public String relativeDir;\n\n    public BackupFlags getFlags() {\n        return new BackupFlags(flags);\n    }\n\n    @NonNull\n    public BackupItems.BackupItem getItem() throws IOException {\n        String relativeDir;\n        if (TextUtils.isEmpty(this.relativeDir)) {\n            if (version >= 5) {\n                // In backup v5 onwards, relativeDir must be set\n                throw new IOException(\"relativeDir not set.\");\n            }\n            // Relative directory needs to be inferred.\n            relativeDir = BackupUtils.getV4RelativeDir(userId, backupName, packageName);\n        } else relativeDir = this.relativeDir;\n        return BackupItems.findBackupItem(relativeDir);\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof Backup)) return false;\n        Backup backup = (Backup) o;\n        return packageName.equals(backup.packageName)\n                && userId == backup.userId\n                && backupName.equals(backup.backupName);\n    }\n\n    @Override","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/db/entity/Backup.java#L74-L110","documentation":"Backup.getItem() must locate the directory (relative to the backup root) where a backup's files live. From backup format version 5 onwards, relativeDir is a mandatory field on the Backup entity — the code can no longer infer it as it did for v4 backups (getV4RelativeDir). If relativeDir is empty/null and version >= 5, the backup record is malformed, so it throws IOException instead of guessing a wrong path.","triggerScenarios":"Calling getItem() (directly or via restore, deleteBackup, isDataDirectoryChanged) on a Backup entity whose relativeDir field is empty or null while version >= 5 — e.g. a v5+ backup created by a buggy/incomplete save path, a hand-edited backup metadata file, or a record deserialized from older JSON missing the new field.","commonSituations":"Restoring or deleting a backup created by a newer App Manager version but imported into metadata that lost the field; custom scripts regenerating backup JSON without relativeDir; schema migration between backup format versions.","solutions":["Recreate the backup so the v5+ metadata is written with relativeDir populated","Manually set relativeDir on the Backup entity (e.g. from the backup's actual folder name) before calling getItem()","If the record actually came from v4, correct the version field to 4 so the v4 inference path (BackupUtils.getV4RelativeDir) is used","Inspect the backup metadata JSON on disk and add the missing relativeDir value"],"exampleFix":"// before\nbackup.setItemRetrieval(); // getItem() throws: relativeDir empty, version=5\n// after\nif (TextUtils.isEmpty(backup.relativeDir) && backup.version >= 5) {\n    backup.relativeDir = findActualBackupDir(backup.backupName); // e.g. \"app/org.example/app-0\"\n}\nbackup.getItem();","handlingStrategy":"validation","validationCode":"if (backup == null || backup.version < 0) throw new IllegalArgumentException(\"Bad backup record\");\nif (backup.version >= 5 && TextUtils.isEmpty(backup.relativeDir)) {\n    // infer, repair, or refuse before calling getItem()\n    backup.relativeDir = BackupUtils.getV4RelativeDir(backup.userId, backup.backupName, backup.packageName);\n}","typeGuard":"boolean hasRelativeDir(Backup b) { return b != null && !TextUtils.isEmpty(b.relativeDir); }","tryCatchPattern":"try {\n    BackupItems.BackupItem item = backup.getItem();\n} catch (IOException e) {\n    Log.e(TAG, \"Backup metadata missing relativeDir (v5+)\", e);\n    promptBackupRepairOrDelete();\n}","preventionTips":["Ensure backup creation code always writes relativeDir for v5+","Validate backup metadata JSON after import/migration","Keep version field consistent with which fields are populated"],"tags":["backup","android","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}