Run session-migration Safely and Debug It
Your desktop theme reset itself after an update and nobody touched a setting, so the culprit is probably a migration script you never knew existed. This guide covers how Ubuntu's session-migration finds its scripts, how to preview the work, and how to run one script explicitly when debugging. Examples target the installed package version 0.3.9build1. Allow about ten minutes; you need a shell and access to the user session being migrated.
The route
Jump straight to the step you need, or tick off Done means at the end.
This tool normally starts automatically near the beginning of a graphical session. It runs executable files from a session-migration scripts directory in ASCII order, and remembers migrations so they are not normally repeated. Treat every script as a state-changing program, even when the wrapper itself looks harmless.
1. Confirm the installed command
Start with an ordinary, unprivileged check:
$ command -v session-migration
/usr/bin/session-migration
$ dpkg-query -W -f='${Package} ${Version}\n' session-migration
session-migration 0.3.9build1
The package installs a user systemd service that runs /usr/bin/session-migration before graphical-session-pre.target. You normally do not need sudo; running it as root can make a migration operate on the wrong home directory or session environment.
Checkpoint
If command -v finds nothing, stop here. Install or repair the package through your normal administration process rather than copying a binary from another machine.
2. Preview the normal script search
Use --dry-run with --verbose before letting anything actually run:
$ session-migration --dry-run --verbose
Directory '/usr/local/share/session-migration/scripts' does not exist, nothing to do
Using '/usr/share/session-migration/scripts' directory
Executing: /usr/share/session-migration/scripts/dark-theme-migration.sh
Executing: /usr/share/session-migration/scripts/nautilus_thumbnail_cache_clear.sh
Directory '/var/lib/snapd/desktop/session-migration/scripts' does not exist, nothing to do
The exact directories depend on XDG_DATA_DIRS in the session. Each data directory is checked for session-migration/scripts; missing ones are reported and skipped, and existing executable files are listed in ASCII order. This test run returned status 0.
The word "Executing" here is misleading if you skim it. --dry-run stops the scripts actually launching and stops them being marked as migrated; the verbose line names the candidate action, not proof anything ran. Check the status immediately when scripting:
session-migration --dry-run --verbose
status=$?
printf 'session-migration status: %s\n' "$status"
exit "$status"
3. Inspect scripts before removing the safety net
Migration scripts are ordinary executable files. Read them and check their permissions before touching anything:
$ find /usr/share/session-migration/scripts -maxdepth 1 -type f -perm /111 -printf '%f\n' | sort
$ sed -n '1,160p' /path/to/session-migration/scripts/example.sh
Do not assume a script is idempotent. The manual says scripts should be safe to run more than once, because the wrapper's timestamp cache is not a substitute for idempotence. A script might alter a desktop setting, move a configuration file, clear a cache, or contact another user-session service.
Recovery
There is no rollback command in session-migration. Before a migration changes a configuration file, back it up in its normal format. For a setting changed through a desktop configuration service, record its current value and use that service's documented command to restore it. Keep the original until the session has been checked.
4. Test one script with --file
To investigate one script, pass its path to --file and keep the dry-run flag:
$ session-migration --dry-run --verbose \
--file /path/to/session-migration/scripts/example.sh
Executing: /path/to/session-migration/scripts/example.sh
--file ignores the normal XDG script directories and considers only the named file. It also does not record that file as migrated. Use an absolute path while debugging so a changed working directory cannot silently select a different file. The file still needs to be executable for a normal migration to pick it up.
Only drop --dry-run once you have read the script and confirmed its backup or recovery path. Run it as the session user:
$ session-migration --verbose \
--file /path/to/session-migration/scripts/example.sh
Executing: /path/to/session-migration/scripts/example.sh
$ printf 'status=%s\n' "$?"
status=0
The wrapper's status reports whether its own processing completed, not that the migration produced the intended desktop state. Verify the changed file or setting with a separate read-only command afterwards.
5. Diagnose the common traps
If a script is listed but does not appear to run on the next session, check whether it has already been recorded as migrated. Do not delete migration state blindly; that can make every previously run migration eligible again. Inspect the package's current data and the script's own logs first, and back up before any state reset.
If no scripts appear at all, compare the session's environment with an interactive shell:
$ printf 'XDG_DATA_DIRS=%s\n' "${XDG_DATA_DIRS-}"
$ find /usr/local/share /usr/share -path '*/session-migration/scripts' -type d -print 2>/dev/null
A shell started outside the graphical session may have different XDG variables, so test from the affected session before concluding a package or script is missing. If one script fails, only run it separately once you understand its contents and side effects. Do not reach for sudo to paper over a permission error without first checking which user owns the session data.
Done means
- Confirmed the installed command and package version.
- Ran a verbose dry run showing the directories and scripts selected for the session.
- Inspected each relevant script for side effects and repeat safety.
- Used
--fileand an absolute path for focused debugging. - Backed up and verified any real migration, with a known recovery path.