July 16, 2026

What's new in tl v0.9.2: a protected email archive, sharper keyword research, and error messages that actually help

A tighter, safer release. Your channel email archive is now protected from accidental overwrites, keyword research reaches deeper into the data to find more matching channels, and when your AI agent hits a query error it gets a real hint instead of a dead end.

> safer edits, sharper research, clearer errors # a tighter tl release, no client update required email archive protected — no accidental overwrites keyword research 5× more channels on domain searches query errors real diagnostic + did-you-mean hints cost warnings silent under 1000 credits per query new command tl profiles update (superuser)

A new release of the tl CLI is out today: v0.9.2. It’s the follow-on to v0.9.0’s big keyword-research rework, and it bundles two versions’ worth of quiet-but-real polish: a safer channel email archive, keyword research that reaches into more of the data, cleaner error messages when an AI agent trips on a query, and warnings that stop nagging about cheap queries. As always, Claude Code, Gemini, Codex, and OpenCode pick everything up automatically the next time you run tl update.

The channel email archive is now protected

Here’s the headline change. If you’ve ever edited a channel record via tl channels update, you might have used all_emails — the field that holds the full history of contact addresses for that channel. That field is now read-only from the CLI. Trying to overwrite it returns a clear “not editable” error.

Why the change: the archive was too easy to accidentally clobber. Passing a shorter list would silently replace the whole archive, quietly dropping every address that wasn’t in the new list. That’s a data-loss shape we don’t want anywhere near a shared record. From now on, the archive is append-only — new addresses are added through an internal, superuser-only tool that never modifies or removes existing entries.

What you can still edit via tl channels update, exactly as before:

  • outreach_email — the current primary contact address (this is the one you actually use for outreach)
  • The channel’s demographic fields
  • The MSN join date

Agent prompts that stay well within the new safety fence:

  • “Update channel UCxxx’s outreach_email to newcontact@brand.com.”
  • “Set the MSN join date on this channel to 2026-05-01.”
  • “Adjust the audience-age split on this channel — I have fresh demographics from the last campaign.”

If you (or your agent) still try to write all_emails from tl channels update, you’ll get a friendly 400 back naming exactly which fields are editable — no silent overwrite, no lost data. And if you genuinely need to add a new address to the archive, ask an admin to run the append tool — it never touches what’s already there.

Keyword research reaches into more of the data

If you’ve used tl-keyword-research recently to find channels that talk about a specific topic, you may have noticed a subtle mismatch: the skill would probe your keyword against one field of channel data, but the actual report filter it built for you would search a broader field surface. For most keywords this didn’t matter. For domain-shaped keywords — things like patreon.com, substack.com, product URLs — it did.

Live check we ran while shipping this: searching for channels that mention patreon.com used to return 1,245 matches. After v0.9.2, it returns 6,375 matches — because the probe now looks at the same domain-aware field the delivered filter searches, instead of only the plain description text. Same query, five times the coverage. If you build filters that lean on brand URLs or platform names, you’ll feel this immediately.

Agent prompts where the difference matters:

  • “Find channels that mention patreon.com in their about text — I want to shortlist creators who cross-promote to Patreon.”
  • “Which finance channels reference substack.com on their channel description? I’m scoping a newsletter co-promo.”
  • “Pull a list of channels that reference our competitor’s domain in their about section.”

Nothing about the workflow changes — you keep asking your agent the same way, and the skill just returns a fuller list.

Error messages that point at the fix

Watching an AI agent trip over a mistyped column name is not fun. Watching it grind through three retries before giving up because the error said only “query failed” is worse. v0.9.2 (and the v0.9.1 changes bundled in) rework the error surface so a failed query lands with a real diagnostic and a concrete next step.

What changed, in agent-visible terms:

  • Elasticsearch query errors now carry the real Elasticsearch reason (unknown field, parsing_exception, the root cause) instead of a generic “Elasticsearch query failed” line — plus a pointer to tl schema es for the live field list.
  • Postgres query errors now name every broken column in a query — not just the first one — with “did you mean X?” suggestions where a rename is the likely culprit.
  • Hints now render on their own yellow “Hint:” line, separate from the red error text. Easy for your agent to see and act on, easy for you to read.
  • tl db pg --help now opens with a “Common traps” preflight — the renamed columns, the joins that catch people out, the “run tl schema pg <table> first” habit — instead of making everyone learn it the hard way.
  • tl db fb --help now spells out the two Firebolt tables, the leading-index filter rule, and four working examples. Failed Firebolt queries return Firebolt’s own diagnostic instead of “Firebolt query failed”.

None of this requires any change to how you ask questions. It just means when things go sideways, the response actually helps your agent get unstuck without burning more credits on shot-in-the-dark retries.

Server warnings surface in the response

New in v0.9.2: tl db responses can now carry advisory _warnings from the server — soft nudges rendered as a yellow “Warning:” line on stderr, in every output mode including --pricing. The classic case: you write an ILIKE query with a leading %, which forces a scan of the whole table, and the server responds with a warning suggesting tl db es for the text-search version. The query still runs; nothing is blocked. Your piped stdout stays clean, so the warnings never break scripts.

Practical effect: when an agent picks the wrong query lane, it now sees the nudge and can course-correct.

Smarter credit warnings

If you’ve been getting the “you’ve already run this query” nudge on cheap or free-tier queries, that stops now. In v0.9.1, the repeat-query warning became spend-aware: it only fires once the same query’s repeats have burned 1000+ credits inside a five-minute window. A cheap query polled in a loop no longer nags. The nudge, when it does fire, now tells you the actual spend so you can decide whether to keep going.

Also dropped: the pre-query “consider adding a limit” nag on unlimited list commands — the default limits already bound the spend far below anything warning-worthy, so the hint was just noise.

Small additions and doc polish

  • New: tl profiles update — a superuser-only command to edit a profile’s internal superuser_notes (free-text account-handling notes, never customer-visible, 2500-char cap). Follows the same pattern as tl sponsorships update and tl channels update. If you’re not an admin, you’ll never see it.
  • tl db fb picks up a SELECT 1 connectivity probe (useful for hooks and quick sanity checks) and clearer help text.
  • Schema docs: every content field in the Elasticsearch reference was re-verified against the live index. The user-visible upshot: when your agent reads the schema before writing a query, the field names, types, and semantics it sees are what actually exists — not what used to exist six months ago.

Updating

Run tl update to pick it all up, or just keep working — the auto-updater will catch you up on your next command. tl changelog shows the running log of what’s landed.

If you’ve been sitting on a keyword research question that leans on brand URLs or platform names, this is the release to ask it in.