MuntashirAkon/AppManager · error · java.lang.IllegalStateException
External directory unavailable.
Error message
External directory unavailable.
What it means
ServerConfig.init copies the AM and main JARs from app assets to the external cache directory (and chmods them). Any failure in that block — typically because external storage is unavailable — is rethrown as an IllegalStateException so callers get a clear 'external directory unavailable' signal.
Source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/servermanager/ServerConfig.java:56
private static final String LOCAL_TOKEN = "l_token";
private static final SharedPreferences sPreferences = ContextUtils.getContext()
.getSharedPreferences("server_config", Context.MODE_PRIVATE);
@WorkerThread
@NoOps
public static void init(@NonNull Context context) throws IOException {
// Setup paths
boolean force = BuildConfig.DEBUG;
try {
File externalCachePath = FileUtils.getExternalCachePath(context);
File externalCacheAmJar = new File(externalCachePath, Constants.JAR_NAME);
File externalCacheMainJar = new File(externalCachePath, Constants.MAIN_JAR_NAME);
AssetsUtils.copyFile(context, Constants.JAR_NAME, externalCacheAmJar, force);
AssetsUtils.copyFile(context, Constants.MAIN_JAR_NAME, externalCacheMainJar, force);
FileUtils.chmod644(externalCacheAmJar);
FileUtils.chmod644(externalCacheMainJar);
} catch (Exception e) {
throw new IllegalStateException("External directory unavailable.", e);
}
try {
File amPath = getAppManagerStoragePath();
File externalAmJar = new File(amPath, Constants.JAR_NAME);
File externalMainJar = new File(amPath, Constants.MAIN_JAR_NAME);
AssetsUtils.copyFile(context, Constants.JAR_NAME, externalAmJar, force);
AssetsUtils.copyFile(context, Constants.MAIN_JAR_NAME, externalMainJar, force);
FileUtils.chmod644(externalAmJar);
FileUtils.chmod644(externalMainJar);
} catch (Exception e) {
e.printStackTrace();
}
}
@NonNull
public static File getAppManagerStoragePath() throws SecurityException {
File externalStorage = Environment.getExternalStorageDirectory();View on GitHub (pinned to 0152f468fc)
Solutions
- Ensure external storage is available (Environment.getExternalStorageState() == MEDIA_MOUNTED) before calling init
- Verify storage permissions (READ/WRITE_EXTERNAL_STORAGE or scoped-storage compliance) are granted
- Remount/reattach storage (disable USB file transfer mode) and retry
- Unwrap the cause 'e' to see the original failure (null path, IOException from copyFile, chmod failure)
Example fix
// before
ServerConfig.init(context, false);
// after
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
ServerConfig.init(context, false);
} else {
throw new IOException("External storage not mounted; cannot init server config");
} Defensive patterns
Strategy: validation
Validate before calling
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new IOException("External storage not mounted");
}
File dir = context.getExternalCacheDir();
if (dir == null || !dir.canWrite()) {
throw new IOException("External cache dir unavailable");
} Try / catch
try {
ServerConfig.init(context, false);
} catch (IllegalStateException e) {
Log.e(TAG, "Server init failed (storage)", e.getCause());
// prompt user to free/enable storage
} Prevention
- Check Environment.getExternalStorageState() before init
- Request and verify storage permissions on legacy Android versions
- Handle the USB-file-transfer (storage unmounted) state in UX
- Unwrap and log getCause() to distinguish null-path vs copy failures
When it happens
Trigger: Calling init when context.getExternalCacheDir() (or the resolved externalCachePath) returns null or the copy/chmod throws, e.g. storage unmounted or permissions revoked.
Common situations: Device in USB mass-storage/MTP mode where external storage is unmounted, scoped-storage restrictions on newer Android versions, or first launch before storage permission granted.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Couldn't find any writable Obb dir
- Could not backup data
- Could not read the whole resource (total = %d, read = %d)
- Could not write the whole resource (total = %d, read = %d)
- Mode ${mode} is not supported.
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/4bc3506518ece143.
Report an issue: GitHub.