diff --git a/CHANGELOG.md b/CHANGELOG.md index daeb120..07491bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to Library Manager will be documented in this file. +## [0.9.0-beta.149] - 2026-04-17 + +### Fixed + +- **Issue #211: Watch-folder failure tracking silently dropped** — Three + `INSERT` statements in `process_watch_folder` (`app.py:6906`, `6914`, + `6944`) referenced `added_at` on the `books` table, but the schema column + is `created_at`. Every insert raised `sqlite3.OperationalError: table + books has no column named added_at`. Two `except` blocks caught it with + `logger.debug`, hiding the error at default log levels. Effects: + - Successful watch-folder moves never produced a `pending` or + `needs_attention` row in the books table. + - Failed watch-folder moves never produced a `watch_folder_error` row — + users had no UI surface for the failure, only a log line. + - Fix: renamed `added_at` to `created_at` in all three INSERTs; + raised both swallow-except blocks from `logger.debug` to + `logger.warning(..., exc_info=True)` so the same class of silent + failure can't rot unnoticed again. + - Surfaced during live testing of #209. Bug has existed since the + watch-folder feature was introduced. + +--- + ## [0.9.0-beta.148] - 2026-04-17 ### Fixed diff --git a/README.md b/README.md index a15d196..e9cd420 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **Smart Audiobook Library Organizer with Multi-Source Metadata & AI Verification** -[![Version](https://img.shields.io/badge/version-0.9.0--beta.148-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-0.9.0--beta.149-blue.svg)](CHANGELOG.md) [![Docker](https://img.shields.io/badge/docker-ghcr.io-blue.svg)](https://ghcr.io/deucebucket/library-manager) [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE) @@ -16,6 +16,10 @@ ## Recent Changes (stable) +> **beta.149** - **Fix: Watch-Folder Move Failures Now Appear in the UI** (Issue #211) +> - Three `INSERT` statements in the watch-folder worker referenced a phantom `added_at` column on the `books` table (the real column is `created_at`). Every insert silently raised `OperationalError`, caught by a `logger.debug` that hid the error. Result: watch-folder move failures never produced a `watch_folder_error` row — the UI never showed the failure, users only saw it in logs. +> - Fixed the column name and raised the swallow-except log level to `warning` with full traceback so future DB errors don't rot silently. + > **beta.148** - **Fix: Watch-Folder Retry Loop Across Restarts + Skaldleita server_notice** (Issue #208) > - **Persistent watch-folder dedup** - `watch_folder_processed` is now a SQLite table instead of an in-memory `set()`. Restarts no longer wipe it, killing the retry loop that had one LM instance hammering Skaldleita's `/match` every 30 seconds on the same file for days. > - **Honors Skaldleita's abort signal** - When the server detects a retry loop it sends a `server_notice` in the response. LM now logs it (with an upgrade URL) and, on `action=abort_task`, stops retrying that file immediately. diff --git a/app.py b/app.py index 6972a4c..fd5962e 100644 --- a/app.py +++ b/app.py @@ -11,7 +11,7 @@ - Multi-provider AI (Gemini, OpenRouter, Ollama) """ -APP_VERSION = "0.9.0-beta.148" +APP_VERSION = "0.9.0-beta.149" GITHUB_REPO = "deucebucket/library-manager" # Your GitHub repo # Versioning Guide: @@ -6903,7 +6903,7 @@ def norm_conf(c): # Unknown author - requires user intervention before processing # Issue #57: Include source_type for watch folder tracking c.execute('''INSERT OR REPLACE INTO books - (path, current_author, current_title, status, error_message, source_type, added_at, updated_at) + (path, current_author, current_title, status, error_message, source_type, created_at, updated_at) VALUES (?, ?, ?, 'needs_attention', ?, 'watch_folder', datetime('now'), datetime('now'))''', (new_path, author, title, 'Watch folder: Could not determine author - please review and correct')) logger.info(f"Watch folder: Flagged for attention (unknown author): {title}") @@ -6911,7 +6911,7 @@ def norm_conf(c): # Known author - normal processing # Issue #57: Include source_type for watch folder tracking c.execute('''INSERT OR REPLACE INTO books - (path, current_author, current_title, status, source_type, added_at, updated_at) + (path, current_author, current_title, status, source_type, created_at, updated_at) VALUES (?, ?, ?, 'pending', 'watch_folder', datetime('now'), datetime('now'))''', (new_path, author, title)) # Issue #126: Auto-enqueue for full pipeline processing @@ -6923,7 +6923,8 @@ def norm_conf(c): logger.info(f"Watch folder: Auto-enqueued for processing: {author}/{title}") conn.commit() except Exception as e: - logger.debug(f"Watch folder: Could not add to books table: {e}") + # Issue #211: was logger.debug which hid the real exception. + logger.warning(f"Watch folder: Could not add to books table: {e}", exc_info=True) else: logger.error(f"Watch folder: Failed to move {item.name}: {error}") # Issue #49: Track failed items in the database so user can see and fix them @@ -6941,13 +6942,15 @@ def norm_conf(c): else: # Insert new record for the failed item c.execute('''INSERT INTO books - (path, current_author, current_title, status, error_message, source_type, added_at, updated_at) + (path, current_author, current_title, status, error_message, source_type, created_at, updated_at) VALUES (?, ?, ?, 'watch_folder_error', ?, 'watch_folder', datetime('now'), datetime('now'))''', (item_path, author, title, f'Watch folder: {error}')) conn.commit() logger.info(f"Watch folder: Tracked failure for user review: {item.name}") except Exception as db_err: - logger.debug(f"Watch folder: Could not track failure in DB: {db_err}") + # Issue #211: was logger.debug which hid the real exception; raise level + # so failures surface in normal operation instead of rotting silently. + logger.warning(f"Watch folder: Could not track failure in DB: {db_err}", exc_info=True) except Exception as e: logger.error(f"Watch folder: Error processing {item_path}: {e}")