golang/go · error

%s:%d: %w

Error message

%s:%d: %w

What it means

The lineErr closure in Engine.Run wraps any error that is not already a *CommandError with the script's filename and line number, giving every script failure a precise location. It fires for errors not tied to a single command's CommandError — script-reader I/O failures, context cancellation, end-of-section log-flush failures, or a custom Cmd that returns a bare error.

Source

Thrown at src/cmd/internal/script/engine.go:193

			_, err = fmt.Fprintf(log, " (%.3fs)\n", time.Since(sectionStart).Seconds())

			if err == nil && (!ok || !e.Quiet) {
				err = s.flushLog(log)
			} else {
				s.log.Reset()
			}
		}

		sectionStart = time.Time{}
		return err
	}

	var lineno int
	lineErr := func(err error) error {
		if _, ok := errors.AsType[*CommandError](err); ok {
			return err
		}
		return fmt.Errorf("%s:%d: %w", file, lineno, err)
	}

	// In case of failure or panic, flush any pending logs for the section.
	defer func() {
		if sErr := endSection(false); sErr != nil && err == nil {
			err = lineErr(sErr)
		}
	}()

	for {
		if err := s.ctx.Err(); err != nil {
			// This error wasn't produced by any particular command,
			// so don't wrap it in a CommandError.
			return lineErr(err)
		}

		line, err := script.ReadString('\n')
		if err == io.EOF {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Read the wrapped error (the %w portion) for the actual root cause and address that.
  2. If the cause is context cancellation, find the hang or increase the test timeout via testing.T.Deadline/-timeout.
  3. If authoring a custom Cmd, wrap failures with cmdError(cmd, err) so they are not double-wrapped and keep their CommandError identity.
  4. If the log writer failed, check the io.Writer passed to Run for faults (closed pipe, full buffer).

Example fix

// before (custom Cmd returns a bare error)
func(s *script.State, args ...string) (script.WaitFunc, error) {
    return nil, errors.New("setup failed")
}
// after
func(s *script.State, args ...string) (script.WaitFunc, error) {
    return nil, cmdError(cmd, errors.New("setup failed"))
}
Defensive patterns

Strategy: try-catch

Try / catch

// When authoring a custom Cmd, wrap errors so they keep CommandError identity
// and are not re-wrapped by lineErr with just a file:line prefix.

runErr := impl.Run(s, cmd.args...)
if runErr != nil {
    return cmdError(cmd, runErr) // preserves CommandError, lineErr leaves it alone
}
return nil

Prevention

When it happens

Trigger: s.ctx.Err() is non-nil (test cancelled/timed out) at engine.go:204; script.ReadString returns an I/O error; endSection fails while flushing logs; or a custom Cmd implementation returns errors.New(...) directly instead of wrapping via cmdError, so lineErr adds the file:line prefix.

Common situations: Test timeout cancelling the script context; a custom command returning a plain error; a log writer (io.Writer) failing; reaching this when debugging which script line caused a panic.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/b516c2d8cfc89c3b. Report an issue: GitHub.