{"record":{"id":"f646c9800603d4fa","repo":"hashicorp/terraform","slug":"ssh-client-is-not-connected","errorCode":null,"errorMessage":"ssh client is not connected","messagePattern":"ssh client is not connected","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/communicator.go","lineNumber":522,"sourceCode":"\t\tif src[len(src)-1] != '/' {\n\t\t\tlog.Printf(\"[DEBUG] No trailing slash, creating the source directory name\")\n\t\t\treturn scpUploadDirProtocol(filepath.Base(src), w, r, uploadEntries)\n\t\t}\n\t\t// Trailing slash, so only upload the contents\n\t\treturn uploadEntries()\n\t}\n\n\tcmd, err := quoteScpCommand([]string{\"scp\", \"-rvt\", dst}, c.connInfo.TargetPlatform)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn c.scpSession(cmd, scpFunc)\n}\n\nfunc (c *Communicator) newSession() (session *ssh.Session, err error) {\n\tlog.Println(\"[DEBUG] opening new ssh session\")\n\tif c.client == nil {\n\t\terr = errors.New(\"ssh client is not connected\")\n\t} else {\n\t\tsession, err = c.client.NewSession()\n\t}\n\n\tif err != nil {\n\t\tlog.Printf(\"[WARN] ssh session open error: '%s', attempting reconnect\", err)\n\t\tif err := c.Connect(nil); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn c.client.NewSession()\n\t}\n\n\treturn session, nil\n}\n\nfunc (c *Communicator) scpSession(scpCommand string, f func(io.Writer, *bufio.Reader) error) error {\n\tsession, err := c.newSession()","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/communicator/ssh/communicator.go#L504-L540","documentation":"Returned by Communicator.newSession (communicator.go:522). When a SCP/SSH command is requested but c.client (the *ssh.Client) is nil — the connection was never established or was torn down — newSession returns this error. Notably it then immediately attempts c.Connect(nil) to reconnect and retries c.client.NewSession(); the caller only sees this message if that reconnect also fails to set up a session.","triggerScenarios":"A provisioner (connection { type=\"ssh\" }) tries to upload a file / run a command before Connect() succeeded, or after the underlying SSH connection dropped and the auto-reconnect in newSession could not re-establish it.","commonSituations":"Remote host unreachable / wrong port; SSH key auth failed so client stayed nil; connection idle-timed out and reconnect failed; provisioner ran before bastion/networking was ready.","solutions":["Verify connectivity: ensure the host/port are reachable and credentials (connection block: host, port, private_key/password) are correct.","For bastion/relay setups, confirm the bastion is reachable and the ProxyJump/bastion_host config is valid.","Increase retries or add a depends_on so the resource network is up before the provisioner runs; check that Connect() succeeds by testing `ssh <host>` manually."],"exampleFix":"# before\n resource \"null_resource\" \"x\" {\n   provisioner \"file\" {  # client nil if host not ready\n     connection { type = \"ssh\" host = aws_instance.w.public_ip }\n   }\n }\n# after: ensure instance/network are ready first\n resource \"null_resource\" \"x\" {\n   depends_on = [aws_internet_gateway.main, aws_instance.w]\n   provisioner \"file\" {\n     connection {\n       type        = \"ssh\"\n       host        = aws_instance.w.public_ip\n       private_key = file(\"~/.ssh/id_rsa\")\n     }\n   }\n }","handlingStrategy":"retry","validationCode":"// Validate connection config and reachability before the provisioner runs.\n if conn.Host == \"\" || !reachable(conn.Host, conn.Port) {\n     return errors.New(\"ssh target not reachable; fix connection block / network\")\n }","typeGuard":null,"tryCatchPattern":"// newSession already reconnects on failure; wrap caller logic to retry once.\n err := communicator.Upload(dst, src)\n if err != nil && strings.Contains(err.Error(), \"not connected\") {\n     if cerr := communicator.Connect(nil); cerr == nil {\n         err = communicator.Upload(dst, src)\n     }\n }","preventionTips":["Use depends_on so the instance/network/bastion are ready before the provisioner runs.","Validate the connection block (host, port, private_key/password, bastion) against a manual ssh test."],"tags":["ssh","provisioner","connection","communicator","network"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}