cli/cli · warning

the asset contains terminal escape sequences; use `--output`

Error message

the asset contains terminal escape sequences; use `--output` to save it to a file, or pass --allow-escape-sequences to output it anyway

What it means

Terminal-safety guard when a downloaded asset is written to stdout ('-'): the content is scanned by iostreams.CopyGuardedContent, and if it contains terminal escape sequences (ANSI CSI sequences) the write is refused unless --allow-escape-sequences was passed. A malicious or accidentally escape-laden file could otherwise manipulate the terminal (title injection, cursor hijacking, even command execution via OSC 52 in some emulators).

Source

Thrown at pkg/cmd/release/download/download.go:427

			)
		}
	}
	return nil
}

// Copy writes the data from r into a file specified by name.
func (w destinationWriter) Copy(name string, r io.Reader) (copyErr error) {
	fp := w.makePath(name)
	if fp == "-" {
		if w.allowEscapes {
			_, copyErr = io.Copy(w.stdout, r)
			return
		}
		copyErr = iostreams.CopyGuardedContent(w.stdout, r, w.isTTY)
		if binErr, ok := errors.AsType[iostreams.BinaryTerminalError](copyErr); ok {
			copyErr = fmt.Errorf("%w; use `--output` to save it to a file, or pass --allow-escape-sequences to output it anyway", binErr)
		} else if errors.Is(copyErr, iostreams.ErrEscapeSequence) {
			copyErr = errors.New("the asset contains terminal escape sequences; use `--output` to save it to a file, or pass --allow-escape-sequences to output it anyway")
		}
		return
	}
	if copyErr = w.check(fp); copyErr != nil {
		return
	}

	if dir := filepath.Dir(fp); dir != "." {
		if copyErr = os.MkdirAll(dir, 0755); copyErr != nil {
			return
		}
	}

	var f *os.File
	if f, copyErr = os.OpenFile(fp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); copyErr != nil {
		return
	}

View on GitHub (pinned to 0eeec0b92e)

Solutions

  1. Save to a file instead: -O asset.log or --output asset.log
  2. If the content is trusted, opt in explicitly: --allow-escape-sequences
  3. Inspect the asset first (download to file, grep for escape bytes) to confirm it is not malicious

Example fix

# before
gh release download v1 -O -   # asset.log contains ANSI escapes
# after
gh release download v1 -O asset.log
Defensive patterns

Strategy: validation

Validate before calling

if stdoutDest && !allowEscapes {
    // download to a temp file, scan, then decide
    tmp, _ := os.CreateTemp("", "asset-")
    defer os.Remove(tmp.Name())
    copyTo(tmp)
    if iostreams.ContainsEscapeSequence(content) { return errors.New("refusing untrusted stdout") }
}

Try / catch

if errors.Is(err, iostreams.ErrEscapeSequence) {
    // either re-run with --output FILE or surface a trust decision to the user
}

Prevention

When it happens

Trigger: gh release download <tag> -O - (or piping to stdout) where the asset bytes contain escape sequences: log files with ANSI colors embedded, crafted malicious files, or test fixtures containing raw \x1b[ sequences.

Common situations: CI pipelines piping release logs to stdout; downloading a colorized build log artifact; security-hardened environments where the guard is intentionally strict. The sibling branch wraps BinaryTerminalError the same way for binary content on a TTY.

Related errors


AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15). Data as JSON: /api/errors/49f53c0b6a427bcf. Report an issue: GitHub.