Skip to content

Instantly share code, notes, and snippets.

@YasienDwieb
Created January 2, 2026 07:00
Show Gist options
  • Select an option

  • Save YasienDwieb/acc8724a5e517e87bf7797c54624201d to your computer and use it in GitHub Desktop.

Select an option

Save YasienDwieb/acc8724a5e517e87bf7797c54624201d to your computer and use it in GitHub Desktop.
Cleanup Stuck Snap applications

Complete Snap Removal Script

A bash script to completely remove stuck snaps from Ubuntu/Linux systems when normal snap remove fails.

Problem

Sometimes snap remove fails and leaves snaps in a broken state with stuck changes in snapd's internal database. Common errors include:

error: snap "package-name" has "remove-snap" change in progress

Even after deleting directories and restarting snapd, the snap remains in a broken state and cannot be removed.

Solution

This script performs a complete nuclear removal by:

  1. ✅ Removing the snap from snapd's internal database (.data.snaps)
  2. ✅ Marking all related changes as Done (status 0)
  3. ✅ Marking all related tasks as Completed (status 4)
  4. ✅ Setting ready-time timestamps on all changes
  5. ✅ Cleaning up remaining directories (~/snap, /var/snap, /snap)
  6. ✅ Verifying complete removal

Requirements

  • Ubuntu/Linux with snapd installed
  • sudo access
  • jq installed (sudo apt install jq)

Usage

Basic Usage

sudo ./complete-snap-removal.sh <snap-name>

Example: Remove stuck "skype" snap

sudo ./complete-snap-removal.sh skype

Example: Remove stuck "slack" snap

sudo ./complete-snap-removal.sh slack

How It Works

The script:

  1. Checks current status - Shows the snap and all related changes
  2. Interactive confirmation - Asks for confirmation before proceeding
  3. Creates timestamped backup - Backs up /var/lib/snapd/state.json
  4. Stops snapd - Safely stops snapd services
  5. Modifies state.json - Updates snapd's internal database using jq
  6. Restarts snapd - With automatic rollback if restart fails
  7. Cleans directories - Removes leftover snap directories
  8. Verifies removal - Confirms snap is completely gone

Safety Features

  • 🔒 Automatic backups - Creates timestamped backup before any changes
  • 🔒 JSON validation - Validates state.json before applying changes
  • 🔒 Automatic rollback - Restores backup if snapd fails to restart
  • 🔒 Interactive confirmation - Shows what will be removed before proceeding
  • 🔒 Non-destructive for other snaps - Only affects the specified snap

Restore from Backup

If something goes wrong, the script shows you how to restore:

sudo systemctl stop snapd.service snapd.socket
sudo cp /var/lib/snapd/state.json.backup-remove-<snap>-<timestamp> /var/lib/snapd/state.json
sudo systemctl start snapd.service snapd.socket

Backups are located at: /var/lib/snapd/state.json.backup-remove-<snap-name>-<timestamp>

What Gets Modified

The script modifies /var/lib/snapd/state.json:

  • Removes snap entry from .data.snaps object
  • Sets change status to 0 (Done) for all related changes
  • Sets task status to 4 (Completed) for all related tasks
  • Sets ready-time to current timestamp for all related changes

These are the same values that snapd uses for successfully completed operations.

Tested On

  • Ubuntu 22.04 LTS
  • Ubuntu 24.04 LTS

Disclaimer

⚠️ This script directly modifies snapd's internal database. While it includes safety features and backups, use at your own risk. Always ensure you have backups of important data.

License

MIT License - Feel free to use, modify, and distribute.

Contributing

Found a bug or have a suggestion? Feel free to open an issue or submit a pull request.

Credits

Created to solve persistent snap removal issues when normal methods fail.

#!/bin/bash
set -e
# Usage function
usage() {
echo "Usage: $0 <snap-name>"
echo ""
echo "Example: $0 skype"
echo ""
echo "This script will:"
echo " 1. Remove the snap from snapd's internal database"
echo " 2. Mark all related changes as Done (status 0)"
echo " 3. Mark all related tasks as Completed (status 4)"
echo " 4. Set ready-time on all changes"
echo " 5. Clean up any remaining directories"
exit 1
}
# Check arguments
if [ $# -ne 1 ]; then
usage
fi
SNAP_NAME="$1"
echo "=== Complete Snap Removal: $SNAP_NAME ==="
echo ""
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "ERROR: This script must be run with sudo"
exit 1
fi
STATE_FILE="/var/lib/snapd/state.json"
BACKUP_FILE="/var/lib/snapd/state.json.backup-remove-${SNAP_NAME}-$(date +%Y%m%d-%H%M%S)"
# Step 1: Check current status
echo "Step 1: Checking current status..."
echo ""
echo "Snap status:"
snap list "$SNAP_NAME" 2>&1 || echo "Not found in snap list"
echo ""
echo "Related changes:"
snap changes | grep -i "$SNAP_NAME" || echo "No changes found"
echo ""
read -p "Continue with complete removal of $SNAP_NAME? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
echo "Aborted."
exit 0
fi
echo ""
# Step 2: Create backup
echo "Step 2: Creating backup..."
cp "$STATE_FILE" "$BACKUP_FILE"
echo "✓ Backup created at: $BACKUP_FILE"
echo ""
# Step 3: Stop snapd
echo "Step 3: Stopping snapd services..."
systemctl stop snapd.service snapd.socket
sleep 2
echo "✓ Snapd stopped"
echo ""
# Step 4: Nuclear edit of state.json
echo "Step 4: Performing complete removal in state.json..."
# Get current timestamp in RFC3339 format
CURRENT_TIME=$(date --rfc-3339=ns | sed 's/ /T/')
jq --arg snap_name "$SNAP_NAME" --arg ready_time "$CURRENT_TIME" '
# Get all task IDs from changes related to this snap
([.changes[] | select(.summary | ascii_downcase | contains($snap_name | ascii_downcase)) | .["task-ids"][]]) as $task_ids |
# Remove snap from the snaps registry
.data.snaps = (.data.snaps | del(.[$snap_name])) |
# Mark all related changes as Done (status 0) and set ready-time
(.changes[] | select(.summary | ascii_downcase | contains($snap_name | ascii_downcase))) |= (
.status = 0 |
."ready-time" = $ready_time
) |
# Mark all related tasks as Completed (status 4)
.tasks |= with_entries(
.key as $k |
if ($task_ids | index($k) != null) then
.value.status = 4
else
.
end
)
' "$STATE_FILE" > "${STATE_FILE}.tmp"
# Verify the edit was successful
if jq empty "${STATE_FILE}.tmp" 2>/dev/null; then
mv "${STATE_FILE}.tmp" "$STATE_FILE"
echo "✓ State file updated successfully"
echo " - Removed $SNAP_NAME from snaps registry"
echo " - Set all related changes to status 0 (Done)"
echo " - Set all related tasks to status 4 (Completed)"
echo " - Set ready-time on all changes"
else
echo "ERROR: Invalid JSON generated, restoring backup"
cp "$BACKUP_FILE" "$STATE_FILE"
rm -f "${STATE_FILE}.tmp"
systemctl start snapd.service snapd.socket
exit 1
fi
echo ""
# Step 5: Restart snapd
echo "Step 5: Restarting snapd services..."
systemctl start snapd.service snapd.socket
sleep 3
# Check if snapd started successfully
if systemctl is-active --quiet snapd.service; then
echo "✓ Snapd restarted successfully"
else
echo "✗ Snapd failed to start, restoring backup..."
systemctl stop snapd.service snapd.socket 2>/dev/null || true
cp "$BACKUP_FILE" "$STATE_FILE"
systemctl start snapd.service snapd.socket
echo "✓ Backup restored"
exit 1
fi
echo ""
# Step 6: Clean up directories
echo "Step 6: Cleaning up directories..."
for dir in "/home/$SUDO_USER/snap/$SNAP_NAME" "/var/snap/$SNAP_NAME" "/snap/$SNAP_NAME"; do
if [ -d "$dir" ]; then
echo "Removing $dir..."
rm -rf "$dir" 2>/dev/null && echo " ✓ Removed" || echo " ⚠ Could not remove (may need manual cleanup)"
fi
done
echo "✓ Directory cleanup complete"
echo ""
# Step 7: Verify removal
echo "Step 7: Verifying complete removal..."
sleep 2
echo "Checking snap list:"
if snap list "$SNAP_NAME" 2>&1 | grep -q "error: no matching snaps installed"; then
echo "$SNAP_NAME is NOT in snap list (SUCCESS!)"
else
snap list "$SNAP_NAME" 2>&1
echo "$SNAP_NAME still appears in snap list"
fi
echo ""
echo "Checking for related changes:"
if snap changes | grep -i "$SNAP_NAME"; then
echo ""
echo "Note: Changes appear with 'Done' status (not blocking)"
else
echo "✓ No $SNAP_NAME changes visible (SUCCESS!)"
fi
echo ""
echo "=== Complete Removal Successful ==="
echo ""
echo "Backup saved at: $BACKUP_FILE"
echo ""
echo "$SNAP_NAME has been completely removed from your system."
echo ""
echo "If you encounter any issues, restore with:"
echo " sudo systemctl stop snapd.service snapd.socket"
echo " sudo cp $BACKUP_FILE $STATE_FILE"
echo " sudo systemctl start snapd.service snapd.socket"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment