tailscale/tailscale · warning

logger closed

Error message

logger closed

What it means

loggingWriter.writeCastLine serializes writes to a recording with r.mu and writes asciicast lines to r.out. After the recording has been closed, r.out is nil, and any late Write from a still-running session process returns 'logger closed'. It marks the point where session output arrived after the recording was finalized.

Source

Thrown at ssh/tailssh/tailssh.go:1673

		if err != nil {
			return 0, err
		}
		j = append(j, '\n')
		if err := w.writeCastLine(j); err != nil {
			if !w.r.failOpen {
				return 0, err
			}
			w.recordingFailedOpen = true
		}
	}
	return w.w.Write(p)
}

func (w loggingWriter) writeCastLine(j []byte) error {
	w.r.mu.Lock()
	defer w.r.mu.Unlock()
	if w.r.out == nil {
		return errors.New("logger closed")
	}
	_, err := w.r.out.Write(j)
	if err != nil {
		return fmt.Errorf("logger Write: %w", err)
	}
	return nil
}

func envValFromList(env []string, wantKey string) (v string) {
	for _, kv := range env {
		if thisKey, v, ok := strings.Cut(kv, "="); ok && envEq(thisKey, wantKey) {
			return v
		}
	}
	return ""
}

// envEq reports whether environment variable a == b for the current

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Ensure the session output pump is stopped (session ctx done, process exited) before closing the recording
  2. Treat this error as non-fatal for session data: the session continues; log it for diagnostics
  3. Upgrade tailscaled — ordering fixes around recording close land periodically
Defensive patterns

Strategy: try-catch

Try / catch

n, err := w.Write(data)
if err != nil && strings.Contains(err.Error(), "logger closed") {
	// output raced recording close; drop, session data unaffected
	return n, nil
}

Prevention

When it happens

Trigger: Session stdout/stderr writes racing recording Close: the child process or SSH channel delivers output after the recording's Close set out to nil.

Common situations: Long or chatty sessions at teardown; bugs where the recording is closed before the session output pump goroutine stops; tests that close recordings while emitters still run.

Related errors


AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15). Data as JSON: /api/errors/6a9df5a13fc5018a. Report an issue: GitHub.