Growth

Your meta description is cut mid-sentence

A description clipped to a character limit is severed mid-clause. Writing to fit whole is the fix, and the field you edit may not be the one that ships.

MobileByteSensei 10 min read
One piece cut through where the cloth stopped, one laid to finish inside it
seopublishingtooling

A search result shows a description that stops partway through a clause. Not abbreviated with an ellipsis by a thoughtful tool — cut, at a character count, between a preposition and the thing it was about to name.

Nothing was broken. The text was longer than the band and something had to go.

The case this post is about

One description, written to 156 characters because 156 was the limit:

const BAND = { min: 140, max: 156 } as const;
/** What the platform will actually render: description, then subtitle, clipped. */
export const generated = (m: { description: string; subtitle: string }) =>
  `${m.description} ${m.subtitle}`.trim().slice(0, BAND.max);

The field I typed was fine. The string the platform rendered was the description plus the subtitle, clipped at 156 — so it ended mid-word, in a clause that had looked complete in the field I was editing.

This post follows that one string, because two things went wrong and only one of them is about length: I wrote to the bound rather than ending before it, and I verified the field I typed instead of the output it was generated into.

The symptom

The visible version is in the search result itself: a sentence that reads perfectly for a hundred and forty characters and then stops, often at the exact point where the reader was about to find out whether the page was worth opening.

The second thing is that it passes every check you have. The description is present, it is the right sort of length, it is not duplicated across pages, and no validator anywhere objects to a sentence that ends abruptly — because a validator counts characters and cannot read.

That is the general shape of the problem and it is worth stating early: every automated check in this area measures length, and the defect is grammatical. A tool can tell you a description is 162 characters. Only a reader can tell you that the 156th character fell in the middle of a noun phrase, which is the part that costs a click.

The third is that the author never sees it. You write the description in a frontmatter field, where it is whole; the truncation happens downstream, in whatever renders the meta tag or in the engine's own display logic. The version you proofread and the version a reader sees are different strings, and nothing puts them side by side.

The fourth is the one that cost me the most time: the field I was editing was not the field that shipped. I had three posts reporting a severed description, fixed each by rewriting the description frontmatter, and watched them come back clean. They came back clean because their subtitles happened to be short enough — the tool derives the output from the subtitle when it has one, and my edits had changed a field that was not in the path at all.

What makes that specific failure worth describing is how convincing the false evidence was. Each post went from a warning to no warning across the edit, which is exactly what a fix looks like. Nothing in the sequence suggested the two events were unrelated, and I only found it by changing a subtitle for an unrelated reason and watching the output move.

Care spent, carefully, on the roll nothing is being cut from

Why the obvious fix failed

Count the characters and stay under. The obvious move, and it treats the limit as a budget to spend to the last unit. A sentence written to end at exactly the bound is one revision away from crossing it, and crossing it does not produce a warning — it produces a cut.

Let the engine truncate; it will do it sensibly. It will not. Truncation happens at a character position, not a syntactic boundary, and the common result is a severed prepositional phrase. Some renderers add an ellipsis, which makes the cut visible without making it less unhelpful.

Write short descriptions to be safe. Works and gives up the surface. A description well under the band leaves usable space unused on every result, and the space is the only thing standing between your page and the one below it.

Fix it when a tool complains. What I did, and it only works if you fix the right field. Three posts' worth of edits went into a field the generator was not reading, and all three reported clean afterwards for an unrelated reason — which is worse than not fixing them, because it produced evidence that the problem was solved.

The actual mechanism

Treat the band as a range to land inside whole, not a limit to approach.

band        roughly 140–156 characters, engine-dependent
whole       the last sentence must END before the upper bound
derive      know which field the output is generated FROM
verify      read the generated string, not the source field

The second line is the entire technique and it is a writing constraint rather than a tooling one. A description that ends at 148 characters with a full stop is correct; one that ends at 155 with a comma is a sentence the reader will not get the end of. Writing to fit whole means the final clause is chosen to land, which is a different act from writing a sentence and then trimming it.

The sub-case that caught me is derivation. Publishing tools commonly generate the output field from one of several sources — a dedicated field if present, otherwise a subtitle, otherwise the first paragraph. Which one is in play is not visible in the file you are editing, and editing the wrong one is indistinguishable from editing the right one until something reports.

There is a related failure worth separating: the description that fits and says nothing. A hundred and fifty characters of competent summary that could sit under any post is within band, never severed, and useless — it occupies the one place where a reader decides whether to click, and spends it restating the title.

Those two failures pull in opposite directions, which is why the band is a range rather than a ceiling. Too long and the sentence is severed; too short or too generic and it says nothing worth clicking. The space between is where a specific claim fits whole, and a description that is both specific and complete is doing the only job the field has.

Two shapes that could only be one thing, and one that fits and is nothing

The fix

write      a final sentence that ends before the bound, not at it
know       which source field the generated output comes from
read       the generated string back, not the field you typed in
treat      a severed report as a bug in the text, not in the tool

The check is on the GENERATED string, not on the field anybody typed — which is the whole point, because the two are different values and only one of them reaches a search result:

const BAND = { min: 140, max: 156 } as const;

/** What the platform will actually render: description, then subtitle, clipped. */
export const generated = (m: { description: string; subtitle: string }) =>
  `${m.description} ${m.subtitle}`.trim().slice(0, BAND.max);

export function auditDescription(m: { description: string; subtitle: string }) {
  const out = generated(m);
  const full = `${m.description} ${m.subtitle}`.trim();

  if (out.length < BAND.min) return { verdict: "SHORT", out, note: `${out.length} < ${BAND.min}` };

  // SEVERED = the clip landed mid-sentence. A word boundary is not enough; the last
  // character has to END a sentence, or the reader sees a dangling clause.
  if (full.length > BAND.max && !/[.!?]$/.test(out.trimEnd())) {
    return { verdict: "SEVERED", out, note: `cut after "${out.slice(-28)}"` };
  }
  return { verdict: "OK", out, note: `${out.length} chars` };
}

A SHORT description is the counter-intuitive failure — it leaves room for the subtitle to be dragged in and then cut:

$ audit posts/three-phases
SEVERED (156)  cut after "and why reading a value in"
  description  61 chars — too short, so the subtitle was pulled in and clipped
  fix          lengthen `description` into the band; do not shorten the subtitle

$ audit posts/three-phases      # after sizing description to 147
OK (147 chars)

The third line is what converts this from a rule you remember to a thing you check. The generated string is available — it is in the rendered page, in the tool's own output, or in the file it writes — and reading it is a two-second habit that makes the derivation question answer itself.

What makes the first line tractable is a trick I resisted for too long: write the description last, from the finished post, rather than first from the plan. A description written before the post exists describes an intention and tends to run long because it is hedging; one written after is a claim about something specific, which is shorter and lands more easily inside a band.

There is a sequencing consequence that follows from that and is easy to miss. If the description is written from the finished post, it cannot be part of the scaffold — which means any tooling that generates frontmatter up front is necessarily producing a placeholder, and a placeholder that is within band and grammatically complete is the most likely thing in the world to survive to publication unnoticed.

How we proved it

The failure is countable across a corpus. A scan of the generated descriptions for a trailing non-terminal character — a comma, a preposition, a conjunction — finds every severed one in a single pass. That turns "I think some of these are cut" into a list, and it is a check that costs nothing to run on every build.

Space unused, space overrun, and space used to the last clause

The wrong-field error is demonstrable, and it is the thing I would check first on anybody else's setup. Change the source field you believe is in play, regenerate, and compare the output. If it did not move, you have been editing a field that is not in the path — which is exactly what I had been doing on three posts while reporting them fixed.

What I cannot tell you is the click-through cost. I have no measurement of what a severed description does to the rate at which people open a result, because I would need a controlled comparison across enough traffic to see it. The argument here is that an unfinished sentence in the one place a reader decides from is obviously bad, not that I have priced it.

What I have not solved: the engine's own rewriting. A search engine frequently ignores your description entirely and synthesises one from the page, which means all of this effort applies to a field that is a suggestion. It is still worth getting right — it is used often enough, and it is the version that appears when the page is shared rather than searched.

And a limitation of the band itself: the numbers are approximate and they differ by engine, by device width and over time. Treating 156 as a hard constant is a convenient fiction; the honest version is that somewhere around 150 your sentence stops being guaranteed, which is another reason to end before it rather than at it.

What I would build if I were doing it again is the check that looks at the last few characters of the generated string rather than its length. A description ending in a comma, a conjunction, a preposition or an article is severed with near-certainty, and that is a far better signal than a count — it finds the real failures and stays quiet about a 158-character sentence that happens to finish cleanly.

The thing I would tell somebody is that this is a writing problem wearing a tooling problem's clothes. Every instinct it provokes is mechanical — count, truncate, warn, automate — and the actual fix is to write a sentence that finishes in the space available, which is an ordinary editorial skill that the character count distracts from entirely.

One thing I got wrong for a long stretch was writing descriptions as summaries. A summary of a technical post compresses badly — it either lists topics or restates the title — whereas a single claim from inside the post fits easily and tells a reader whether they disagree. The band stopped being difficult when the thing going into it changed.

What this generalizes to

A limit that silently truncates is a different thing from a limit that refuses, and almost all the damage comes from which of the two you are dealing with.

The shape is everywhere once you look. A log line cut at a buffer size, mid-stack-trace. A database column that silently truncates on write in one mode and errors in another. A notification body clipped by the operating system. A commit message subject cut by a display. In each case the system accepted the input, reported success, and delivered something the author did not write.

So the useful question about any bounded field is what happens at the boundary — refusal, truncation or wrapping — because that single fact determines whether the bound is something you can write up to, or something you must deliberately finish before.

One post a week, no digest

New writing when it lands. No roundups, no course pitch, no "10 tips".

Follow by RSS