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
- Save to a file instead: -O asset.log or --output asset.log
- If the content is trusted, opt in explicitly: --allow-escape-sequences
- 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
- Default to -O <file> in scripts; reserve stdout piping for trusted artifacts
- Only pass --allow-escape-sequences for artifacts whose provenance you control
- Scan logs/artifacts for ESC bytes (grep -P '\x1b') in CI before displaying them
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
- file contains terminal escape sequences; use --allow-escape-
- gist file contains terminal escape sequences; pass --allow-e
- the diff contains terminal escape sequences; pass --allow-es
- release %q with tag %q, does not have a %q archive asset.
- no assets match the file pattern
AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15).
Data as JSON: /api/errors/49f53c0b6a427bcf.
Report an issue: GitHub.