Skip to content

fix(trim): guard coerceIn to prevent crash on clips shorter than MIN_TRIM_DURATION#96

Closed
claude[bot] wants to merge 1 commit into
mainfrom
feature/crashlytics-7169b499
Closed

fix(trim): guard coerceIn to prevent crash on clips shorter than MIN_TRIM_DURATION#96
claude[bot] wants to merge 1 commit into
mainfrom
feature/crashlytics-7169b499

Conversation

@claude

@claude claude Bot commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Fixes #95

Crash: java.lang.IllegalArgumentException: Cannot coerce value to an empty range: maximum 335 is less than minimum 400 in FilmstripTrimSelector (Crashlytics issue 7169b499fee6554684dba69b1ae1a8f0, first seen v1.0.27).

Root cause: When a clip shorter than MIN_TRIM_DURATION (400 ms) reached the Trim screen, the drag handler and accessibility callbacks called:

// END handle — throws when durationMs < minGapMs
targetMs.coerceIn(curStartMs + minGapMs, durationMs)  // e.g. coerceIn(400, 335)
// START handle — throws when endMs < minGapMs
targetMs.coerceIn(0L, curEndMs - minGapMs)            // e.g. coerceIn(0, -65)

Kotlin's Long.coerceIn throws IllegalArgumentException on inverted ranges.

Fix: Pre-clamp each derived bound so the range is always valid, regardless of clip length:

// END handle
val minMs = (curStartMs + minGapMs).coerceAtMost(durationMs)
val clamped = targetMs.coerceIn(minMs, durationMs)

// START handle
val maxMs = (curEndMs - minGapMs).coerceAtLeast(0L)
val clamped = targetMs.coerceIn(0L, maxMs)

When the clip is shorter than MIN_TRIM_DURATION both handles become immovable, which is correct — the NEXT button is already disabled by trimValid in TrimScreen.kt.

Also: Adds docs/lessons_learned/025-trim-coercein-range-guard-short-clip.md documenting the pattern.

Files changed

File Change
ui/components/TrimFilmstripControls.kt Guard all 4 coerceIn calls with inverted-range-safe bounds
docs/lessons_learned/025-trim-coercein-range-guard-short-clip.md New lesson
docs/lessons_learned/README.md Index entry for lesson 025

DEFINITION_OF_DONE — steps NOT run on CI

Per docs/DEFINITION_OF_DONE.md, the following could not be verified in this automated triage:

  • Release build (./gradlew :app:assembleRelease) — CI has no signing keys
  • Emulator run + screenshot — no emulator in this environment; manually launch the Trim screen with a sub-400 ms clip and confirm no crash
  • Instrumented tests — not run; no connected device/emulator in CI
  • Android Lint clean (./gradlew :app:lintDebug) — not run

Debug build and unit tests should pass in CI.

Manual QA checklist

  • Record a very short clip (tap shutter and immediately release, < 400 ms) — confirm no crash on the Trim screen
  • Drag both trim handles while on a short clip — confirm handles are immovable (frozen at 0 / durationMs) and no crash
  • Use TalkBack / accessibility services to interact with trim handles on a short clip — confirm no crash
  • Record a normal clip (≥ 400 ms) — confirm trim handles still work as expected
  • Import a short video from gallery — confirm no crash on Trim screen

DRAFT — do not merge. Requires emulator smoke-test and manual QA before marking ready.

When a clip shorter than MIN_TRIM_DURATION (400 ms) reached the Trim
screen, FilmstripTrimSelector called coerceIn(400, 335) — an inverted
range — throwing IllegalArgumentException. Guard each arithmetically-
derived bound before the call so the range is never inverted regardless
of clip length. Adds Lesson 025.

Fixes #95

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

stozo04 commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Superseded by #97, which applies the same guard via a pure, JVM-testable TrimHandleMath.kt (all four call sites) and adds a 12-test regression suite verified to reproduce the exact production crash against the unguarded logic. Recommend closing this draft in favor of #97.


Generated by Claude Code

@stozo04 stozo04 closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Crashlytics] io.github.stozo04.openloop.ui.components.TrimFilmstripControlsKt$FilmstripTrimSelector$1$14$1.invoke$lambda$3

1 participant