wavetermdev/waveterm · error

bad response from server: questions has len %d, echos has le

Error message

bad response from server: questions has len %d, echos has len %d

What it means

The keyboard-interactive auth callback validates that the server sent equal-length 'questions' and 'echos' arrays before prompting the user. A mismatch means the server produced a malformed keyboard-interactive challenge, so the client cannot map answers back to prompts and aborts with this error. It protects against misbehaving or non-standard SSH servers.

Source

Thrown at pkg/remote/sshclient.go:431

		if err != nil {
			blocklogger.Infof(connCtx, "[conndebug] ERROR Password Authentication failed: %v\n", SimpleMessageFromPossibleConnectionError(err))
			return "", ConnectionError{ConnectionDebugInfo: debugInfo, Err: err}
		}
		blocklogger.Infof(connCtx, "[conndebug] got password from user, sending to ssh\n")
		return response.Text, nil
	}
}

func createInteractiveKbdInteractiveChallenge(connCtx context.Context, remoteName string, debugInfo *ConnectionDebugInfo) func(name, instruction string, questions []string, echos []bool) (answers []string, err error) {
	return func(name, instruction string, questions []string, echos []bool) (answers []string, outErr error) {
		defer func() {
			panicErr := panichandler.PanicHandler("sshclient:kbdinteractive-callback", recover())
			if panicErr != nil {
				outErr = panicErr
			}
		}()
		if len(questions) != len(echos) {
			return nil, fmt.Errorf("bad response from server: questions has len %d, echos has len %d", len(questions), len(echos))
		}
		for i, question := range questions {
			echo := echos[i]
			answer, err := promptChallengeQuestion(connCtx, question, echo, remoteName)
			if err != nil {
				return nil, ConnectionError{ConnectionDebugInfo: debugInfo, Err: utilds.MakeCodedError(ConnErrCode_UserCancelled, err)}
			}
			answers = append(answers, answer)
		}
		return answers, nil
	}
}

func promptChallengeQuestion(connCtx context.Context, question string, echo bool, remoteName string) (answer string, err error) {
	// limited to 15 seconds for some reason. this should be investigated more
	// in the future
	ctx, cancelFn := context.WithTimeout(connCtx, 60*time.Second)
	defer cancelFn()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Update the SSH server / appliance firmware to a version with correct keyboard-interactive support.
  2. Disable keyboard-interactive auth on the server (PasswordAuthentication or pubkey-only) so a compliant method is used.
  3. Connect directly to the host if the malformed challenge comes from an intermediate jump/bastion device.
  4. If you control the PAM config, fix or replace the MFA module returning mismatched prompts.

Example fix

// before (sshd_config on server)
KbdInteractiveAuthentication yes
// after
KbdInteractiveAuthentication no  # or fix the PAM module emitting mismatched prompts
Defensive patterns

Strategy: retry

Validate before calling

// probe the server before connecting
conn, err := ssh.Dial("tcp", host, ssh.ClientConfig{...})
// if you get this error deterministically, the server's kbd-interactive is broken

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "bad response from server") {
        // retry with a ClientConfig that disables KeyboardInteractiveChallenge,
        // relying on password/publickey auth
    }
}

Prevention

When it happens

Trigger: Connecting to an SSH server (or intermediate gateway/MFA device) whose keyboard-interactive implementation returns challenges where len(questions) != len(echos), e.g. a buggy PAM stack or custom SSH server.

Common situations: Connecting through VPN/MFA gateways, multi-factor PAM modules, or non-OpenSSH servers (some appliances, older firmware) that emit inconsistent kbd-interactive packets.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/1a8032033541bb44. Report an issue: GitHub.