{"record":{"id":"e30b4ed853b146ca","repo":"airbnb/epoxy","slug":"tried-to-restore-instance-state-but-onsaveinstancestate-was","errorCode":null,"errorMessage":"Tried to restore instance state, but onSaveInstanceState was never called.","messagePattern":"Tried to restore instance state, but onSaveInstanceState was never called\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/BaseEpoxyAdapter.java","lineNumber":250,"sourceCode":"      throw new IllegalStateException(\"Must have stable ids when saving view holder state\");\n    }\n\n    outState.putParcelable(SAVED_STATE_ARG_VIEW_HOLDERS, viewHolderState);\n  }\n\n  public void onRestoreInstanceState(@Nullable Bundle inState) {\n    // To simplify things we enforce that state is restored before views are bound, otherwise it\n    // is more difficult to update view state once they are bound\n    if (boundViewHolders.size() > 0) {\n      throw new IllegalStateException(\n          \"State cannot be restored once views have been bound. It should be done before adding \"\n              + \"the adapter to the recycler view.\");\n    }\n\n    if (inState != null) {\n      viewHolderState = inState.getParcelable(SAVED_STATE_ARG_VIEW_HOLDERS);\n      if (viewHolderState == null) {\n        throw new IllegalStateException(\n            \"Tried to restore instance state, but onSaveInstanceState was never called.\");\n      }\n    }\n  }\n\n  /**\n   * Finds the position of the given model in the list. Doesn't use indexOf to avoid unnecessary\n   * equals() calls since we're looking for the same object instance.\n   *\n   * @return The position of the given model in the current models list, or -1 if the model can't be\n   * found.\n   */\n  protected int getModelPosition(EpoxyModel<?> model) {\n    int size = getCurrentModels().size();\n    for (int i = 0; i < size; i++) {\n      if (model == getCurrentModels().get(i)) {\n        return i;\n      }","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/BaseEpoxyAdapter.java#L232-L268","documentation":"onRestoreInstanceState reads the saved parcelable from the bundle; a non-null bundle without the SAVED_STATE_ARG_VIEW_HOLDERS key means onSaveInstanceState was never called for this adapter, so there is nothing to restore. The library throws to surface the missing save step rather than silently skipping restoration.","triggerScenarios":"Calling onRestoreInstanceState with a Bundle that was produced by a different adapter, a fresh empty Bundle, or a bundle where the state key is absent (viewHolderState == null).","commonSituations":"Saving state on one EpoxyRecyclerView/adapter and restoring on another; passing a manually created Bundle; state was lost because onSaveInstanceState threw earlier (e.g. error [0]) so the key was never written.","solutions":["Ensure you call onSaveInstanceState(Bundle) (or recyclerView.saveState()) on the same adapter before restoring, and store the resulting bundle.","Verify the same adapter/RecyclerView instance saves and restores state — state is not shared across adapters.","Check that the earlier save actually succeeded (it can throw if stable ids are missing).","If restore is optional, guard the call: only restore when the bundle contains the state key."],"exampleFix":"// before\nBundle empty = new Bundle();\nadapter.onRestoreInstanceState(empty); // throws: never saved\n\n// after\nBundle saved = new Bundle();\nadapter.onSaveInstanceState(saved);\nadapter.onRestoreInstanceState(saved);","handlingStrategy":"validation","validationCode":"if (bundle != null && bundle.containsKey(\"saved_view_holders\")) { adapter.onRestoreInstanceState(bundle); } // or always pair save+restore on the same instance","typeGuard":null,"tryCatchPattern":"try { adapter.onRestoreInstanceState(bundle); } catch (IllegalStateException e) { // state was never saved; continue without restoration }","preventionTips":["Always call onSaveInstanceState on the same adapter you restore from.","Keep save/restore calls paired in the lifecycle (onSaveInstanceState -> onRestore/onCreate).","Guard optional restoration with a containsKey check."],"tags":["android","epoxy","state-restoration","missing-state"],"backgroundTag":"resource-not-found","analyzedSha":"e45bd3a61fe3a1f130e184f5b8dcf172ab99025a","analyzedAt":"2026-09-13T03:24:16.052Z","contentChangedAt":"2026-09-13T03:24:16.052Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}