Skip to content

fix crashes on migrateGSESavesToSteamUserdata#1207

Merged
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
joshuatam:fix/gse-saves-migration-crashes
Apr 13, 2026
Merged

fix crashes on migrateGSESavesToSteamUserdata#1207
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
joshuatam:fix/gse-saves-migration-crashes

Conversation

@joshuatam
Copy link
Copy Markdown
Contributor

@joshuatam joshuatam commented Apr 13, 2026

Description

Quote from discord: COPY_ATTRIBUTES is not a valid/supported option for Files.move on Android’s NIO implementation, so Files.move throws UnsupportedOperationException immediately. The code only catches IOException, so the exception escapes and crashes the app.

Recording

None

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Fixes crashes when migrating GSE saves to Steam userdata on Android. Removes an unsupported move option, preserves file timestamps manually, and hardens error handling.

  • Bug Fixes
    • Removed StandardCopyOption.COPY_ATTRIBUTES from Files.move (unsupported on Android); set timestamp via setLastModifiedTime.
    • Catch Exception instead of only IOException so UnsupportedOperationException is handled and migration continues.
    • Keep StandardCopyOption.ATOMIC_MOVE and create parent directories before moving.

Written for commit fa5ef46. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Improved save file migration to better preserve file metadata during transfers to Steam directories.
    • Enhanced error handling during save migration to catch a broader range of issues and provide more reliable failure reporting.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

The migrateGSESavesToSteamUserdata function in SteamUtils.kt now explicitly preserves file modification timestamps after moving files by capturing the timestamp before the move and reapplying it afterward, replacing implicit attribute copying. Exception handling broadened from IOException to Exception.

Changes

Cohort / File(s) Summary
Timestamp Preservation & Exception Handling
app/src/main/java/app/gamenative/utils/SteamUtils.kt
Modified save migration to explicitly capture and restore file modification timestamps after moving files (replacing StandardCopyOption.COPY_ATTRIBUTES), and expanded per-file failure handler to catch Exception instead of just IOException.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~5 minutes

Possibly related PRs

  • PR #1100: Introduces the original migrateGSESavesToSteamUserdata routine that this PR now refines for explicit timestamp preservation and broader exception handling.

Poem

🐰 A rabbit hops through files with care,
Timestamps tucked and waiting there,
No copy dance, just plain and true,
Each moment saved—the move shines new!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing crashes in the migrateGSESavesToSteamUserdata function by addressing an Android compatibility issue.
Description check ✅ Passed The description includes a clear explanation of the problem (COPY_ATTRIBUTES unsupported on Android), the solution (removing it and catching Exception instead), and an auto-generated summary. However, the checklist shows the recording checkbox is unchecked despite the template requiring it.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/utils/SteamUtils.kt (1)

929-934: Implement atomic move fallback for improved compatibility on filesystems without atomic move support.

At lines 929–934, Files.move(...) with ATOMIC_MOVE will throw AtomicMoveNotSupportedException if the filesystem cannot guarantee atomicity. The recommended pattern is to retry without the flag; retrying with just REPLACE_EXISTING falls back to copy+delete. Also narrow the catch block from Exception to IOException and SecurityException (currently line 941–943), and make timestamp preservation non-fatal to improve resilience:

Suggested approach
+import java.nio.file.AtomicMoveNotSupportedException

 try {
     Files.createDirectories(targetFile.parent)
     val fileTimestamp = file.lastModified()

-    Files.move(
-        file.toPath(),
-        targetFile,
-        StandardCopyOption.REPLACE_EXISTING,
-        StandardCopyOption.ATOMIC_MOVE
-    )
+    try {
+        Files.move(
+            file.toPath(),
+            targetFile,
+            StandardCopyOption.REPLACE_EXISTING,
+            StandardCopyOption.ATOMIC_MOVE
+        )
+    } catch (_: AtomicMoveNotSupportedException) {
+        Files.move(
+            file.toPath(),
+            targetFile,
+            StandardCopyOption.REPLACE_EXISTING
+        )
+    }

-    targetFile.setLastModifiedTime(FileTime.fromMillis(fileTimestamp))
+    runCatching {
+        targetFile.setLastModifiedTime(FileTime.fromMillis(fileTimestamp))
+    }.onFailure { err ->
+        Timber.tag("migrateGSESavesToSteamUserdata")
+            .w(err, "Migrated ${file.name} but failed to preserve timestamp")
+    }

     Timber.tag("migrateGSESavesToSteamUserdata").i("Migrated ${file.name} from GSE saves to Steam userdata")
     migratedCount++
-} catch (e: Exception) {
+} catch (e: IOException) {
+    migrationFailed = true
+    Timber.tag("migrateGSESavesToSteamUserdata").w(e, "Failed to migrate ${file.name}")
+} catch (e: SecurityException) {
     migrationFailed = true
     Timber.tag("migrateGSESavesToSteamUserdata").w(e, "Failed to migrate ${file.name}")
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/app/gamenative/utils/SteamUtils.kt` around lines 929 - 934,
The Files.move call using StandardCopyOption.ATOMIC_MOVE can throw
AtomicMoveNotSupportedException; update the move logic in SteamUtils (the block
calling Files.move(file.toPath(), targetFile,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE)) to catch
AtomicMoveNotSupportedException and retry the move without ATOMIC_MOVE (i.e.,
only REPLACE_EXISTING) so the operation falls back to copy+delete on non-atomic
filesystems, and narrow exception handling to catch IOException and
SecurityException rather than a broad Exception; also make timestamp
preservation (where file times are copied) non-fatal by logging failures instead
of propagating exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/app/gamenative/utils/SteamUtils.kt`:
- Around line 929-934: The Files.move call using StandardCopyOption.ATOMIC_MOVE
can throw AtomicMoveNotSupportedException; update the move logic in SteamUtils
(the block calling Files.move(file.toPath(), targetFile,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE)) to catch
AtomicMoveNotSupportedException and retry the move without ATOMIC_MOVE (i.e.,
only REPLACE_EXISTING) so the operation falls back to copy+delete on non-atomic
filesystems, and narrow exception handling to catch IOException and
SecurityException rather than a broad Exception; also make timestamp
preservation (where file times are copied) non-fatal by logging failures instead
of propagating exceptions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10af7074-422b-4a25-bbd5-37cd3baacad8

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8401c and fa5ef46.

📒 Files selected for processing (1)
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

@utkarshdalal utkarshdalal merged commit a29bb89 into utkarshdalal:master Apr 13, 2026
3 checks passed
@joshuatam joshuatam deleted the fix/gse-saves-migration-crashes branch April 14, 2026 01:09
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.

2 participants