Publishing & Deployment Issues

Unity Input System Rebinding JSON Not Loading on Android - File Path and Persistent Data Migration Fix

Fix Unity Input System rebinding JSON not loading on Android by validating persistentDataPath usage, scoped storage-safe file migration, and deterministic save-load timing.

By GamineAI Team

If your custom controls work in Editor but reset to defaults on Android, the binding override JSON is usually being read from the wrong path, loaded before the file is available, or blocked by legacy storage assumptions.

This fix path gives you a deterministic Android-safe flow: save and load binding overrides from Application.persistentDataPath, migrate old files once, and only apply overrides after your InputActionAsset is initialized.

Problem summary

Common symptoms:

  • Rebinds are successful during gameplay, but restart returns to default controls.
  • Android logs show file-not-found or empty JSON when loading overrides.
  • Rebinding works on Windows or in Editor but fails on APK or AAB builds.
  • Controls reset after app update, package rename, or save-data migration.

Impact:

  • accessibility and player preference settings are lost
  • QA cannot verify control persistence across builds
  • release confidence drops for mobile input reliability

Root causes

  1. Wrong save/load location on Android Saving to temporary or editor-only paths instead of Application.persistentDataPath.

  2. Scoped storage mismatch Legacy external-storage assumptions break on newer Android versions.

  3. Load-order race Applying overrides before actions/maps are enabled or before JSON read completes.

  4. No migration for legacy filename or folder changes Existing users keep old JSON in a previous location after app updates.

  5. Silent parse failure Invalid or truncated JSON is read without validation, so defaults stay active.

Step-by-step fix

Step 1 - Standardize one persistent path contract

Use one canonical file path:

  1. define rebindFilePath = Path.Combine(Application.persistentDataPath, "input_rebinds.json")
  2. use the same path for both save and load
  3. log the resolved path in development builds for diagnostics

Do not use StreamingAssets, temporaryCachePath, or editor-only relative paths for user-authored rebind data.

Step 2 - Save and load only Input System binding overrides

For Unity Input System:

  1. save with InputActionAsset.SaveBindingOverridesAsJson()
  2. load with InputActionAsset.LoadBindingOverridesFromJson(jsonString)
  3. keep this data separate from unrelated profile JSON

This avoids schema drift when non-input profile data changes.

Step 3 - Add one-time legacy migration on startup

Before loading active overrides:

  1. check old known locations or filenames used by prior builds
  2. if legacy file exists and new file does not, copy legacy file to canonical path
  3. write one migration marker so copy runs only once

Run migration before any call to LoadBindingOverridesFromJson.

Step 4 - Validate file presence and JSON integrity before apply

Safe load sequence:

  1. File.Exists(rebindFilePath) guard
  2. File.ReadAllText with exception handling
  3. reject empty or whitespace-only payload
  4. parse/apply overrides inside guarded block
  5. on failure, keep defaults and log reason with file size and timestamp

Failing closed with explicit logs is better than silently applying partial state.

Step 5 - Apply overrides after input actions are ready

Load timing should occur after action asset initialization:

  1. initialize your input singleton or player input wrapper
  2. ensure action maps are available
  3. apply loaded overrides
  4. enable gameplay maps

If you apply too early in bootstrap, overrides may appear to load but be overwritten by later initialization.

Verification checklist

  • [ ] Rebind on Android device, restart app, and confirm controls persist.
  • [ ] Upgrade from previous build and verify legacy migration preserves existing rebinds.
  • [ ] Log confirms canonical persistentDataPath and successful JSON apply.
  • [ ] Corrupt JSON test falls back to defaults with explicit warning logs.
  • [ ] No editor-only or external-storage path is required for runtime persistence.

Alternative fixes for edge cases

  • If rebinds are profile-specific, suffix filename by user slot (for example input_rebinds_slot_2.json) while keeping the same canonical directory.
  • If cloud sync is enabled, merge local file first, then apply cloud patch to avoid wiping recent offline rebinds.
  • If multiple InputActionAsset instances exist, centralize save/load in one owner to avoid last-write conflicts.

Prevention tips

  • Keep one versioned wrapper for rebind JSON so future format changes are migration-safe.
  • Add Android smoke test in CI or QA checklist: rebind -> force close -> relaunch -> verify.
  • Record rebind load outcome metrics (success, missing file, parse fail, migration used) in release telemetry.
  • Avoid changing filename/path conventions during late release windows without migration logic.

FAQ

Why does rebinding work in Editor but not on Android build

Editor often reads from different writable locations and has fewer storage restrictions. Android runtime requires explicit persistent path handling and stable startup load order.

Should we request storage permissions for rebinding JSON

Usually no. For app-internal persistence, Application.persistentDataPath is the correct approach and does not require broad external storage permissions.

Can we store rebinding data in PlayerPrefs instead

You can for small payloads, but JSON file persistence is easier to migrate, inspect, and validate across versions and profiles.

Related links

Bookmark this fix before your next Android submission pass, and share it with your gameplay and QA teammates if it helps stabilize control persistence.