microsoft / microsoft/go-sqlcmd

-r discards the "Msg N, Level N, State N" error header — errors on stderr lose message number and line info

Open
#794 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
595
Forks
91
Avg merge
9h 35m
Merged PRs (30d)
1

Description

Description

When -r (errors to stderr) is specified, server error messages are written to stderr
as bare message text only — the Msg %d, Level %d, State %d, Server %s, Line %d
header line is silently dropped. Without -r, the header prints correctly (to stdout).

ODBC sqlcmd with -r prints the same formatted output (header + message), just
redirected to stderr. So scripts/CI pipelines migrating from ODBC sqlcmd lose the error
number and line information exactly in the configuration meant for error capture.

Repro
-- repro.sql
SELECT id, badColumn FROM sys.objects WHERE object_id = 1;
GO
> sqlcmd -S myserver -E -b -i repro.sql          # no -r
Msg 207, Level 16, State 1, Server MYSERVER, Line 1
Invalid column name 'id'.

> sqlcmd -S myserver -E -r -b -i repro.sql       # with -r
Invalid column name 'id.'        <-- stderr: message text only, no Msg/Level/State/Line

ODBC sqlcmd (16.0.1000.6) with the identical -r -b flags writes to stderr:

Msg 207, Level 16, State 1, Server MYSERVER, Line 1
Invalid column name 'id'.
Msg 207, Level 16, State 1, Server MYSERVER, Line 1
Invalid column name 'badColumn'.
Root cause (from source)

Formatter.AddError in pkg/sqlcmd/format.go formats the header correctly for
mssql.Error. But when -r is passed, cmd/sqlcmd/sqlcmd.go installs a
s.PrintError hook that receives only e.Message and writes it directly to
os.Stderr, returning true — which bypasses Format.AddError entirely:

s.PrintError = func(msg string, severity uint8) bool {
    if severity >= stderrSeverity {
        s.WriteError(os.Stderr, errors.New(msg+sqlcmd.SqlcmdEol))
        return true
    }
    return false
}

Suggested fix: format the full header inside the hook (or route through the formatter
with a stderr destination) so -r only changes the destination, not the format —
matching ODBC sqlcmd behavior.

Additional observation

Combined with -b, only the first error of a batch is reported (the message loop
exits on the first qualifying error), while ODBC sqlcmd prints all errors of the
response before exiting. Together with the missing header this significantly degrades
error diagnostics for deployment scripts.

Environment
  • sqlcmd v1.10.0 (sqlcmd-windows-amd64.zip from Releases)
  • Windows 11, SQL Server 2022 (16.0.1190.2)
  • Compared against ODBC sqlcmd 16.0.1000.6 with identical flags
  • (Server messages in our environment are localized/Korean; behavior is language-independent)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in cmd/sqlcmd/sqlcmd.go at the -r PrintError hook, then read Formatter.AddError in pkg/sqlcmd/format.go to compare the existing header formatting. Reproduce the commands in the issue and verify that -r sends the same Msg, Level, State, Server, and Line header plus message to stderr, while preserving the existing destination behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
cli, database
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.