# Compose Nested Scrollable Crash — "infinity maximum height constraints"

**Crash:** `java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed.`

## Root Cause

`LazyColumn` and `LazyVerticalGrid` require a **fixed or bounded height**. When
placed inside a parent that has `.verticalScroll()` (e.g., `Modifier.verticalScroll(rememberScrollState())`),
the parent's scrollable container measures children with `Constraints(maxHeight = Infinity)`.
The `LazyColumn` receives infinity height and crashes immediately.

**Why this doesn't crash with plain `Column`:** Plain `Column` handles unbounded
height by measuring all children in a single pass. `LazyColumn` cannot — its lazy
measurement/decomposition contract requires a bounded height.

## Diagnostic Signature

The exception stack trace is **all internal Compose** — you will NOT see your
screen class in the trace. The trace points to `SubcomposeLayout` / `LazyLayout`
/ `LazyColumnMeasurePolicy`. To find the actual screen:

```bash
# Find files with BOTH verticalScroll AND LazyColumn/LazyVerticalGrid
grep -rnl "verticalScroll" app/src/main/java/ --include='*.kt' | xargs grep -l "LazyColumn\|LazyVerticalGrid"
```

This command lists files that contain both patterns — those are crash candidates.
Then check if the `LazyColumn` is a child (direct or indirect via composition) of
the composable with `.verticalScroll()`.

## Most Common Unspooled Pattern

**Settings/Details two-pane layout** — the right pane has `.verticalScroll()` wrapping
content that includes sub-screens with `LazyColumn`:

```kotlin
// CRASHES when selected panel has LazyColumn:
Box(Modifier.weight(1f).fillMaxHeight().verticalScroll(rememberScrollState())) {
    when (selected) {
        Panel.SOURCE_PREFS -> SourcePreferencesScreen()  // ← has LazyColumn
        Panel.ADDONS -> AddonsScreen()                   // ← has LazyColumn
        Panel.CATALOGS -> CatalogManagerScreen()          // ← has LazyColumn
    }
}
```

## Fix

**Remove `.verticalScroll()` from the outer container.** Each inner screen MUST
handle its own scrolling independently:

```kotlin
// FIXED — no crash:
Box(Modifier.weight(1f).fillMaxHeight()) {
    when (selected) {
        Panel.SOURCE_PREFS -> SourcePreferencesScreen()  // ← LazyColumn inside
        Panel.ADDONS -> AddonsScreen()                   // ← LazyColumn inside
        Panel.CATALOGS -> CatalogManagerScreen()          // ← LazyColumn inside
        Panel.DIAGNOSTICS -> DiagnosticsContent()         // ← plain Column (short, no scroll needed)
    }
}
```

If an inner screen has a plain `Column` with no scrolling and the content might
overflow, add `.verticalScroll()` to that column specifically — not the outer
container.

## Also Check

- **No `Column(Modifier.verticalScroll())` inside another `verticalScroll()` column**
  — nested `.verticalScroll()` creates `ScrollableColumn` inside `ScrollableColumn`,
  which causes scroll-jacking (the outer scroll steals scroll events from the inner).
- **No `LazyColumn` inside `Column(Modifier.verticalScroll())`** — this is the exact
  pattern that crashes.

## Full Audit Command

```bash
cd ~/Unspooled
# Find ALL verticalScroll + LazyColumn coexistence in source
grep -rn "verticalScroll\|LazyColumn\|LazyVerticalGrid" app/src/main/java/ --include='*.kt' \
  | grep -v "import \|^\s*\*" \
  | awk -F: '{print $1}' | sort | uniq -c | sort -rn
```

Files with count ≥3 likely have both scroll mechanisms. Inspect each.

## Fixed Instance (June 2026)

`app/src/main/java/com/unspooled/app/ui/screens/settings/SettingsScreen.kt:203`

Removed `.verticalScroll(rememberScrollState())` from outer right-pane `Box`.
All inner settings panels (DebridServicesScreen, TraktServicesScreen, SourcePreferencesScreen,
AddonsScreen, CatalogManagerScreen, etc.) already have their own scrolling
(`.verticalScroll()` on root Column or `LazyColumn`).
