Skip to content

Build1 publisher3 min readPublished Updated

Exit 0 only means nothing threw: what a logged-out browser costs a cron job

An Instagram automation lane reported success twelve times a day while doing nothing. The fix was asserting on live session state, read out of Chrome's cookie database, not on the absence of errors.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Exit 0 only means nothing threw: what a logged-out browser costs a cron job
Generated illustration

What happened

  • The lane tied to the account ig-2 in the author's social-autolike system was scheduled to run 12 times a day; each run logged "complete" and no errors reached the monitoring dashboard.
  • The reality was likes = 0 and follows = 0; the job had been doing absolutely nothing for days.
  • The cause was that the ig-2 account was logged out and its Instagram session had expired.
  • The job, staring at a logout screen, kept returning exit 0 on the grounds that "there were no targets left" or "no accounts matched the criteria".
  • The job was working normally, or appeared to be, until the morning of 2026-08-09.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A scheduled browser-automation lane ran twelve times a day, logged "complete" on every run, sent no errors to its monitoring dashboard, and produced zero likes and zero follows for days [1][2]. The account it drove, `ig-2`, had been logged out and its Instagram session had expired; the job, parked on a logout screen, kept returning exit 0 on the reasoning that there were no targets left or no accounts matched the criteria [3][4].

The developer, writing on dev.to, says the lane looked normal until the morning of 2026-08-09 [5]. There was login-detection code. `needLogin()` in `social-autolike/src/run.js` flagged a login as required if the URL contained `/login`, or if the body contained the Japanese word for login and none of `article`, `[role=feed]`, `video`, `main` or `[data-e2e]` were present [6]. Instagram's logout screen contains a `main` element, so the second condition never fired: one `main` was found, and the check passed the logged-out page as healthy [7].

That is the whole failure in miniature. A selector answers "does this page look like a login screen," which is not the question; the question is whether this browser is currently authenticated, and the first proxy dies the moment the markup changes [8].

The replacement assertion reads state instead of appearance. Chrome keeps session cookies in a SQLite file at `Default/Network/Cookies`, or `Default/Cookies` on older versions [9]. Instagram's login state lives in a cookie named `sessionid`; if that cookie exists for the `instagram.com` domain, the profile is logged in [10]. The author's argument for making cookies the primary source is that the mapping holds regardless of redesigns to the logout page [11]. Anything driving a Chrome or Chromium profile is exposed to the same gap, whether the driver is Playwright, Puppeteer, browser-use, or something assembled with Claude Code [12].

The guard script, `profile-session-guard.sh`, runs before the work and stops the lane with a notification when the session is dead [15]. Four stages: resolve the profile directory list from three config files (`accounts.json`, `ig-reply-accounts.json`, `post-accounts.json`) plus two fixed entries from a separate repo, collapsing duplicates [16]; look for the cookie DB at `Default/Network/Cookies`, `Default/Cookies`, `Network/Cookies` or `Cookies`, and copy it with `mktemp`, deleting the copy on exit [17]; query the copy with `sqlite3` using a per-platform SQL statement and grade each profile OK, EXPIRED or UNKNOWN [18]; then aggregate, compare against a state file, skip the notification when the breakage matches the previous run, and on a new breakage send a Discord message and exit 1 [19].

The cost of not doing this is measured in silence. A graph pinned at zero is hard to notice while no errors fire, and the author could not reconstruct how many days `ig-2` had lost [13][14]. At twelve runs a day, a single week of that is eighty-four consecutive green runs [20].

Two things to watch if you copy the pattern. UNKNOWN is a real third outcome in this design, not a rounding error, and it needs a policy: treat it as expired and you halt on a moved database file, treat it as fine and you are back to guessing [18][9]. And the state-file deduplication that stops Discord from repeating itself also means a lane that stays broken tells you once [19], so the durable check is the inverse of the one that failed here: alert on a lane that has produced nothing while claiming success, not on a lane that threw.

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories