a flowing river in the middle of the forest

Fixing What Was Quietly Wrong

   

Written by:

There’s a certain satisfaction in finding a bug that’s been hiding in plain sight. No crash, no obvious error message — just something subtly wrong that finally gets tracked down and fixed. Here are five open-source contributions from the past several days, all squarely in the “this was broken and now it isn’t” category.


Kometa: Fix trakt_chart URL patterns for watched, collected, and recommended

Kometa is a Python tool that automatically manages your Plex library — building smart collections, applying poster overlays, and pulling in metadata from sources like Trakt, IMDb, and MDBList. It’s one of those “set it and forget it” tools that quietly does an enormous amount of work.

Three Trakt chart types — watched, collected, and recommended — had broken API URLs. The _charts() method was assembling path fragments incorrectly for all three:

  • watched and collected both require a time period in the URL (like /movies/watched/weekly) — which wasn’t being included
  • recommended was hitting /movies/charts/recommended instead of the correct /recommendations/movies endpoint

The fix rewrites URL construction to handle each pattern explicitly:

if chart_type == "recommended":
    chart_url = f"/recommendations/{media}"
elif time_period:
    chart_url = f"/{media}/{chart_type}/{time_period}"
else:
    chart_url = f"/{media}/{chart_type}"

A nightly branch had already attempted a partial fix — but introduced a double-slash bug in the process. Partial fixes that don’t account for all callers can be worse than no fix. Tracing the full call chain before touching anything was what caught it.


FlareSolverr: Preserve numeric types when postData is JSON

FlareSolverr is a headless Chrome proxy that helps automated tools get past Cloudflare and similar bot-detection challenges. It’s widely used in the self-hosted media ecosystem.

When postData was forwarded to a target API, FlareSolverr always submitted it via an HTML <form>. That works fine for URL-encoded data — but HTML form submissions are strings-only by nature. JSON APIs that type-check their payloads (expecting feeRate: 50 as a number) would receive "50" instead and throw a validation error. Users had no workaround.

The fix: when postData is detected as valid JSON, a new _json_post_request helper takes over. It:

  1. Navigates to the target URL (GET) to establish a same-origin browser context
  2. Fires a fetch() call via execute_async_script with Content-Type: application/json
  3. Writes the response back with document.write() — same output contract as before

The same-origin context from step 1 is what lets the fetch() call bypass CORS without needing any special headers — a subtle detail that made the whole approach much cleaner than the alternatives.


Homebrew Cask: Fix the Pinegrow source URL

Homebrew Cask is the community-maintained registry of macOS apps for Homebrew. Every app has a .rb cask file that specifies where to download it, how to verify the checksum, and how to detect new versions.

The Pinegrow cask was pointing to download.pinegrow.com — a CDN that had quietly started redirecting to GitHub Releases. Homebrew’s audit tools flagged the URL as broken, and the livecheck block couldn’t determine the current version.

Following the redirect chain led to github.com/Pinegrow/PinegrowReleases. The URL, both architecture checksums, and the livecheck block were updated to point there directly. A couple of Homebrew-specific details worth noting:

  • The verified: parameter is required when the download URL domain differs from the homepage — it tells Homebrew the cross-domain link is intentional
  • The livecheck regex strips the pg prefix from Pinegrow’s release tag format (pg7.17.1)

WP Performance: Treat zero-size and no-store responses as errors

The WordPress Performance plugin ships a Site Health audit that checks your enqueued CSS and JavaScript assets for performance problems. It’s great — but it had two cases where it was quietly waving things through that it really shouldn’t have.

  • Zero-size body: A 200 response with an empty body was reported as a 0-byte asset (all good!). It is not all good.
  • Cache-Control: no-store: An asset that browsers are forbidden from caching was reported as fine, as long as the status code was 200.

Both now return WP_Error and show as red rows in the audit table. One deliberate non-change: Cache-Control: no-cache (revalidate) still passes — an asset that revalidates can still be served from cache, which is meaningfully different from no-store. Both error results feed into the existing display path, so no UI changes were needed.


Unbalance: Reset speed samples before each new rsync process

Unbalance is an Unraid plugin for redistributing files between drives in your NAS array. It wraps rsync and shows a rolling transfer speed estimate while operations run.

When a move operation spans multiple files, each spawns a separate rsync process. Linux’s /proc/<pid>/io byte counters start fresh at 0 for every new PID. The speed calculator was computing a delta against PrevSample — but PrevSample was never reset between processes.

When the second rsync started with transferred = 0 and PrevSample = 500 GB, a uint64 subtraction wrapped around to approximately 1.8×10¹⁰ MB. Result: displayed speeds of 1,700 MB/s on spinning hard drives.

A resetSamples() function already existed in the codebase. It was just never called. One line at the start of runCommand() fixed it. Classic case of dead code that was written for exactly this scenario and then forgotten about — which is somehow both frustrating and satisfying to find.

Leave a Reply

Discover more from EnRoute

Subscribe now to keep reading and get access to the full archive.

Continue reading