skip to content

What is wrong with the Go error string "failed to sync: failed to fetch: connection refused"?

level: middleimportance: should knowfreq 52%

answer

  1. the reader already knows it failed
  2. count the words that identify nothing
  3. three tokens of padding per layer
  4. each layer: its operation and its object
  5. the colon already means inside that

basics

~10 s

Every layer prepends "failed to", which adds nothing: the value is an error, so failure is already asserted. The repetition displaces the identifiers a reader needs and stops the chain reading as one sentence.

solid answer

~40 s

The problem is that `failed to` is noise repeated at every level. An `error` value already means the operation failed, so the words carry no information; what they do carry is three tokens of padding before each meaningful noun, so the reader scans past `failed to` three times to reach `connection refused`. It also breaks the reading: the chain is supposed to render as one sentence narrowing from the broadest operation to the specific cause, and repeated verbs of failure make it read as three separate announcements. The fix is that each `fmt.Errorf` layer names *its own operation and its object* — `sync origin`, `fetch refs`, `dial 10.0.0.5:9418` — so the rendered line is `sync origin: fetch refs: dial 10.0.0.5:9418: connection refused`. Same length, every token now identifying, and the distinctive parts are searchable.

code

go · 13 lines
go
func dialRemote(addr string) error {
	if err := dial(addr); err != nil {
		return fmt.Errorf("dial %s: %w", addr, err)
	}
	return nil
}

func fetchRefs(addr string) error {
	if err := dialRemote(addr); err != nil {
		return fmt.Errorf("fetch refs: %w", err)
	}
	return nil
}

go deeper

for a junior

Recognise the repeated prefix as a defect and be able to rewrite one layer: replace failed to X with the operation and the thing it acted on, lowercase and unpunctuated.

for a middle

Explain the mechanic — colon-joined clauses narrowing left to right — and show that the padding displaces identifiers. Rewriting the example into sync origin: fetch refs: dial address: cause is the expected answer.

for a senior

Talk about consistency at codebase scale: one verb style, an identifier in every clause, and why a pasted message must lead a triager to a specific line of source rather than a hundred generic matches.

for a principal

Own it as a written convention with examples, and be able to say why you would rather standardise the clause shape than let each team invent its own message voice across services.

## The symptom A command-line tool prints: ``` failed to sync: failed to fetch: failed to dial: connection refused ``` Nothing here is factually wrong. It is nevertheless the single most common style defect in Go error text, and it is worth being able to explain precisely why rather than as a matter of taste. ## Why the prefix is empty The value being rendered is an `error`. Its existence is the assertion that something failed. Writing `failed to` into the text restates the type of the value in prose — the equivalent of naming a boolean `isTrueOrFalse`. Once, at the top, a handler may legitimately announce failure (`myprog: sync origin: ... `, or a top-level `error: ` prefix). Repeated at every layer, it is pure padding. Count the tokens. Of the ten words in the example, six are the same two words repeated. The parts a reader actually needs — which operation, on what, and what went wrong at the bottom — are `sync`, `fetch`, `dial`, `connection refused`, and the example does not even say *which* remote or *which* address. ## What a layer should say instead The convention is: **each wrapping layer contributes the operation it attempted, plus the object that makes it identifiable.** A verb and its noun, lowercase, no punctuation: ``` sync origin: fetch refs: dial 10.0.0.5:9418: connection refused ``` Read left to right, that is one sentence that narrows: the command was syncing the remote called `origin`; part of that was fetching refs; part of that was dialling this address; the network said no. The colon-space separator is doing the work that `failed to` was pretending to do — it means "and specifically, inside that". Notice what the rewrite gained without getting longer: two concrete identifiers (`origin`, `10.0.0.5:9418`) now occupy the space the repeated verbs used to. That is the real cost of the padding — it is not just ugly, it displaces information. ## The one-sentence test A quick check before you commit a message: imagine your clause with an arbitrary prefix in front of it and an arbitrary cause after it, and read the whole thing aloud. If it still reads as one narrowing sentence, the clause is right. If it introduces a new subject, restarts with a capital, announces failure again, or ends with punctuation, it is wrong. This test is why the guidance about lowercase, no trailing period and no `failed to` are really the same rule seen from three angles: the clause must survive being placed in the middle. ## Near neighbours of the same defect - **`error:` inside the text.** Produces `error: sync origin: error: ...` under a top-level handler that adds its own prefix. - **Restating the cause.** A layer that writes `dial failed: connection refused: %w` duplicates the tail it is already carrying. Add what only *you* know — the address, the ref name, the operation — not a paraphrase of what you were handed. - **Passive constructions.** `unable to be completed` is longer and vaguer than `sync origin`. - **Varying the verb per layer.** `could not`, `unable to`, `failed to` mixed through one codebase makes the same failure render three different ways and defeats searching. ## Why searchability matters here The person who eventually reads this line is often triaging a pasted bug report. They select a distinctive fragment and search the codebase for it. `failed to fetch` appears in a hundred programs and, in a large codebase, in a dozen places of your own. `fetch refs` appears where you wrote it. Uniform, information-dense clauses are what make a pasted message lead to a line of source in seconds instead of minutes. ## A note on what this is not about This is a question about the *wording* each layer contributes, not about how many layers should wrap or which verb links them. A message can be perfectly worded and still be wrapped at too many levels, and a chain wrapped at exactly the right levels can still read as `failed to: failed to: failed to`. Fix the words; the layering is a separate discussion.

  • If not "failed to", what exactly should a single fmt.Errorf layer's text contain?
    The operation that layer attempted and the object it attempted it on: `fetch refs`, `dial 10.0.0.5:9418`, `open config`. A short verb-noun clause, lowercase, unpunctuated, adding the identifier only that layer knows. Nothing about failure, and no paraphrase of the cause it is carrying.
  • Does dropping "failed to" lose any information?
    None. The value is an error, so failure is already asserted; the concrete cause still sits at the tail of the chain. What you gain is room for identifiers — a remote name, an address, a path — that turn a generic complaint into something a reader can act on.
  • Should the top-level handler that prints the error announce failure at all?
    Usually yes, exactly once: a program prefix and possibly the word error, added where the message is finally rendered. That is a printing decision made in one place, which is very different from every intermediate layer repeating it into the text.

saying these in an interview costs you the question

  • Argues the reader needs the words failed to to know it failed
  • Adds error: into the message text at every layer
  • Restates the wrapped cause in the new prefix
  • Mixes could not, unable to and failed to across one codebase
  • Treats the wording as taste and cannot name the information cost
  • Writes a prefix with no identifier, such as dial rather than dial the address