diff --git a/Watchdogs/docker_watchdog.sh b/Watchdogs/docker_watchdog.sh index 12cb930..b4ff684 100755 --- a/Watchdogs/docker_watchdog.sh +++ b/Watchdogs/docker_watchdog.sh @@ -676,6 +676,26 @@ CYCLE_START=$(date +%s) done # ── Skip list and intentional stops visibility ─────────────────────────────────────────── + # Prune entries for containers that no longer exist at all (uninstalled/removed) from both + # state files. Neither remove_from_skip_list() nor clear_intentional_stop() can ever fire + # for one of these — both only trigger when a container is "seen running again," which never + # happens for something that's been uninstalled — so without this an entry nags every cycle + # forever (confirmed live 2026-07-19: Healarr, uninstalled weeks earlier, still flagged every + # run). Snapshot into an array first — sed -i rewriting the same file a `while read < file` + # loop is still iterating is the classic gotcha this avoids. + for _prune_file in "$DOCKER_WATCHDOG_FAILED_FILE" "$DOCKER_WATCHDOG_INTENTIONAL_FILE"; do + [[ -s "$_prune_file" ]] || continue + mapfile -t _prune_snapshot < "$_prune_file" + for _prune_container in "${_prune_snapshot[@]}"; do + [[ -z "$_prune_container" ]] && continue + if ! timeout "$DOCKER_TIMEOUT" docker inspect "$_prune_container" &>/dev/null; then + sed -i "/^${_prune_container}$/d" "$_prune_file" 2>/dev/null + warn "$_prune_container no longer exists — removed from $(basename "$_prune_file")" + fi + done + done + unset _prune_file _prune_snapshot _prune_container + _skip_contents=$(cat "$DOCKER_WATCHDOG_FAILED_FILE" 2>/dev/null | tr '\n' ' ' | xargs) [[ -n "$_skip_contents" ]] && warn "$ICON_SKIP Skip list active: $_skip_contents — manual intervention needed" _intentional_contents=$(cat "$DOCKER_WATCHDOG_INTENTIONAL_FILE" 2>/dev/null | tr '\n' ' ' | xargs)