projectdiscovery/nuclei · error

sending password: %w

Error message

sending password: %w

What it means

Writing the password line to the telnet connection failed. After the password prompt matched, Client.Auth calls writeLine(ctx, password); the returned error wraps the underlying net.Conn write failure (broken pipe, connection reset, closed conn, or ctx canceled). This is a transport-level failure, not an authentication verdict.

Source

Thrown at pkg/utils/telnetmini/telnet.go:222

// Auth performs a minimal Telnet username/password interaction.
// It waits for a username/login prompt, sends username, waits for a password prompt,
// sends password, and then looks for fail banners or shell prompts.
// A timeout should be enforced via ctx.
func (c *Client) Auth(ctx context.Context, username, password string) error {
	// Wait for username/login prompt
	if _, _, err := c.readUntil(ctx, c.UserPrompts...); err != nil {
		return fmt.Errorf("waiting for login/username prompt: %w", err)
	}
	if err := c.writeLine(ctx, username); err != nil {
		return fmt.Errorf("sending username: %w", err)
	}

	// Wait for password prompt
	if _, _, err := c.readUntil(ctx, c.PasswordPrompts...); err != nil {
		return fmt.Errorf("waiting for password prompt: %w", err)
	}
	if err := c.writeLine(ctx, password); err != nil {
		return fmt.Errorf("sending password: %w", err)
	}

	// Post-auth: look quickly for explicit failure, else accept shell prompt / silence.
	match, got, err := c.readUntil(ctx,
		append(append([]string{}, c.FailBanners...), c.ShellPrompts...)...,
	)
	if err != nil && !errors.Is(err, context.DeadlineExceeded) {
		return fmt.Errorf("post-auth read: %s (got: %s)", preview(got, 200), err)
	}
	low := strings.ToLower(match)
	for _, fb := range c.FailBanners {
		if low == strings.ToLower(fb) {
			return errors.New("authentication failed")
		}
	}
	// success (matched a shell prompt or timed out without explicit failure)
	return nil
}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Retry with a fresh Client/connection — a mid-handshake reset is usually transient or a session-limit signal.
  2. Check whether the server allows more than one concurrent telnet session and close stale sessions.
  3. Ensure ctx is not already expired when Auth is called (the same ctx bounds every read and write in the handshake).
  4. If the server closes on username, revisit UserPrompts — the wrong value may have been sent as the username.

Example fix

// before
_ = client.Auth(ctx, user, pass)

// after
if err := client.Auth(ctx, user, pass); err != nil {
    if strings.Contains(err.Error(), "sending password") {
        client.Close()
        // reconnect once; server dropped us mid-handshake
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// nothing to validate client-side beyond connection liveness
if err := client.Ping(ctx); err != nil { /* skip Auth, target is unhealthy */ }

Type guard

func isTelnetWriteErr(err error) bool {
    return err != nil && strings.HasPrefix(err.Error(), "sending password:")
}

Try / catch

if err := client.Auth(ctx, user, pass); err != nil {
    if isTelnetWriteErr(err) {
        client.Close()
        // one reconnect+retry; a mid-handshake reset is often transient or a session cap
    }
}

Prevention

When it happens

Trigger: Server closes the connection immediately after printing the password prompt (some services do this on max sessions or bad TTY negotiation), ctx is canceled/expired before the write, or the conn was already torn down by the remote end or an intermediary.

Common situations: Remote device enforcing a single telnet session (kicks the new one), NAT/idle timeout killing the session mid-handshake, or test harnesses against mock listeners that accept but immediately close.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/2d517800d1f0b815. Report an issue: GitHub.