Skip to content

fix(ssh): stop warning about sessions that were never going to be recorded - #6896

Merged
otavio merged 1 commit into
masterfrom
fix/ssh/recording-skip-log-level
Aug 13, 2026
Merged

fix(ssh): stop warning about sessions that were never going to be recorded#6896
otavio merged 1 commit into
masterfrom
fix/ssh/recording-skip-log-level

Conversation

@otavio

@otavio otavio commented Aug 12, 2026

Copy link
Copy Markdown
Member

What

Session.Recorded now reports the two conditions under which a session was never going to be
recorded as expected outcomes rather than failures, so pipe logs them at debug instead of
warning. Recording behaviour is unchanged.

Why

Recorded returned an undifferentiated error for three unrelated outcomes: recording disabled
for the namespace, a seat with no pty, and a genuine UpdateSession failure. The only call site
logged every non-nil return at warning as failed to set the session as recorded, so both
expected cases were reported as a compliance feature failing.

On a fleet whose sessions are almost entirely non-interactive automation (ssh host <command>),
that is every session — an observed deployment logged the warning for 66 of 66 sessions in a
99-minute window, which triggered an incident investigation before the error string was read
closely enough to show the condition was normal.

Closes #6894

Changes

  • session: added ErrRecordingDisabled and ErrRecordingNoPty, both wrapping
    ErrRecordingSkipped. Wrapping keeps the specific reason in the log's error field while
    letting a caller ask a single question — a third skip condition won't require touching any
    caller. The sentinels live beside Recorded rather than in errors.go, whose contents are
    scoped to errors returned to the SSH client.
  • channels: pipe branches on errors.Is(err, session.ErrRecordingSkipped), logging
    session recording skipped at debug and reserving the warning for a real UpdateSession
    failure. recorder = nil still runs for all three outcomes.
  • tests: TestRecorded covers the four outcomes and asserts sentinel identity with
    errors.Is, not message text, since identity is what the call site dispatches on.
    TestPipeReportsExpectedRecordingSkips covers the defect itself — an enterprise-edition pipe
    plus a logrus hook, asserting nothing is emitted at warn level and the skip lands at debug.

The signature was kept, rather than moving to (bool, error), because the reason for the skip is
what makes the debug line worth reading.

Testing

The warning branch has no call-site test: Session.service is unexported, so the channels
package cannot inject a failing service. Its error origin is covered by TestRecorded's
"marking the session as recorded fails" case in the session package.

go test ./ssh/session/ ./ssh/server/channels/ -count=1
golangci-lint run ./ssh/...

…orded

`Session.Recorded` returned a plain error both when recording was off for the
namespace and when the seat had no pty, so the only call site — which logs every
non-nil return at warning as "failed to set the session as recorded" — reported
the expected cases as failures. On a fleet running non-interactive SSH that is
every session: an operator reading the logs concludes a compliance feature is
silently dropping recordings.

Return `ErrRecordingDisabled` and `ErrRecordingNoPty`, both wrapping
`ErrRecordingSkipped` so the caller needs a single comparison, and log them at
debug. Warning is left for a genuine `UpdateSession` failure. The recorder is
still dropped in all three cases, so behaviour is unchanged.

Fixes: #6894
@otavio
otavio requested a review from a team as a code owner August 12, 2026 21:19
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Claude finished @otavio's task in 3m 11s —— View job


Code Review Complete

Reviewed 4 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is.

A few things I specifically verified:

  • Error wrapping / dispatchErrRecordingDisabled and ErrRecordingNoPty both wrap ErrRecordingSkipped via %w, so errors.Is(err, session.ErrRecordingSkipped) at the pipe call site routes both expected cases to Debug. errors and fmt are already imported.
  • No audit gap — a genuine UpdateSession failure returns the raw store error, which does not wrap ErrRecordingSkipped, so it still lands on Warning("failed to set the session as recorded"). Only the two "never going to be recorded" outcomes were downgraded; recording behaviour is unchanged.
  • Test correctness — the asserted debug message "session will not be recorded" matches what pipe emits, and seat 0 is valid in both test setups. No t.Parallel() is used in these packages, so the global logrus hook / edition swaps don't race.
  • No other callersSession.Recorded has a single production call site, and nothing in this repo or cloud/ matches on the old error strings, so removing them is safe.

Clean, well-scoped fix with tests covering both the sentinel identities and the regression itself.


To request another review round, comment /review.
· fix/ssh/recording-skip-log-level

@otavio
otavio merged commit 8057f65 into master Aug 13, 2026
40 checks passed
@otavio
otavio deleted the fix/ssh/recording-skip-log-level branch August 13, 2026 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(ssh): "failed to set the session as recorded" logged at warning for expected non-PTY and recording-disabled sessions

1 participant