cli/cli · warning
file contains terminal escape sequences; use --allow-escape-
Error message
file contains terminal escape sequences; use --allow-escape-sequences to read anyway
What it means
Terminal-safety guard in `gh repo read-file`: before writing fetched file content to output, the bytes are scanned with iostreams.ContainsEscapeSequence, and if any terminal escape sequences are present the write is refused unless --allow-escape-sequences was passed. Unlike the release-download guard this applies in BOTH TTY and non-TTY modes, because even piped output typically ends up in some downstream terminal, and a malicious repo file could otherwise inject ANSI/OSC sequences.
Source
Thrown at pkg/cmd/repo/read-file/read_file.go:199
// read-file does its own escape-sequence guarding below, so it writes raw
// bytes through ContentOut in passthrough mode. Leaving sanitization on
// would corrupt binary files and strip the escapes that
// --allow-escape-sequences explicitly allows.
opts.IO.SetContentSanitization(false)
if mime, ok := iostreams.BinaryContentType(file.Content); ok {
if opts.IO.IsStdoutTTY() {
return fmt.Errorf("binary file (%s, %s); use --output to save to a file or pipe stdout",
mime, text.FormatSize(int64(file.Size)))
}
_, err = opts.IO.ContentOut.Write(file.Content)
return err
}
// Refuse terminal escape sequences unless --allow-escape-sequences, in both TTY and non-TTY modes,
// so a malicious file cannot manipulate a downstream terminal.
if !opts.AllowEscapeSequences && iostreams.ContainsEscapeSequence(file.Content) {
return errors.New("file contains terminal escape sequences; use --allow-escape-sequences to read anyway")
}
if opts.IO.IsStdoutTTY() {
if err := opts.IO.StartPager(); err != nil {
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
}
defer opts.IO.StopPager()
}
_, err = opts.IO.ContentOut.Write(file.Content)
return err
}
// loadContent fetches the raw file bytes when the Contents API did not return them inline.
// The API only omits inline content for large files, which it marks with a "none" encoding;
// everything else (including empty files) comes back base64-encoded, so there is nothing to fetch.
func loadContent(httpClient *http.Client, repo ghrepo.Interface, file *repoFile, ref string) error {
if file.Encoding != "none" {View on GitHub (pinned to 0eeec0b92e)
Solutions
- If you trust the file, opt in: gh repo read-file owner/repo/FILE --allow-escape-sequences
- Otherwise save to a file instead of stdout with --output, then inspect with a tool that renders escapes safely
- Scan the file content for 0x1b bytes first if provenance is unknown
Example fix
# before gh repo read-file org/repo/build.log # after gh repo read-file org/repo/build.log --allow-escape-sequences
Defensive patterns
Strategy: validation
Validate before calling
content := fetchFile(owner, repo, path)
if !opts.AllowEscapeSequences && iostreams.ContainsEscapeSequence(content) {
return errors.New("file contains escape sequences; pass --allow-escape-sequences or --output FILE")
} Try / catch
if err != nil && strings.Contains(err.Error(), "terminal escape sequences") {
if trusted {
err = rerunWithFlag("--allow-escape-sequences")
} else {
err = rerunWithOutput(tmpFile) // inspect safely
}
} Prevention
- Use --output FILE when reading untrusted repo files, then inspect offline
- Reserve --allow-escape-sequences for files you authored or audited
- Treat escape-sequence hits as a security signal when browsing unknown repos
When it happens
Trigger: Reading a file from a repository whose bytes contain ESC (0x1b) followed by [ or ] sequences: files with embedded ANSI colors, crafted malicious repo content, or binary-ish text files that happen to contain 0x1b. Applies to both `gh repo read-file owner/repo/file` and programmatic use.
Common situations: Reading a repo-hosted .log, .txt, or dotfile with color codes; CI that cats repo files through gh; security-conscious teams scanning untrusted repos. The comment in the source explicitly states the refusal covers non-TTY mode too.
Related errors
- the asset contains terminal escape sequences; use `--output`
- gist file contains terminal escape sequences; pass --allow-e
- the diff contains terminal escape sequences; pass --allow-es
- invalid URL: %s
- invalid JupyterLab server URL: %q
AI-assisted analysis of cli/cli@0eeec0b92e (2026-08-15).
Data as JSON: /api/errors/4fa85335a8da1d13.
Report an issue: GitHub.