{"record":{"id":"19417e77e617a9e5","repo":"pulumi/pulumi","slug":"failure-reading-plugin-v-read-v-s-19417e","errorCode":null,"errorMessage":"failure reading plugin [%v] (read '%v'): %s","messagePattern":"failure reading plugin \\[(.+?)\\] \\(read '(.+?)'\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdk/go/common/resource/plugin/plugin.go","lineNumber":416,"sourceCode":"\n\t\t\t\t// If theres no error from waiting then just report the EOF\n\t\t\t\tif readerr == nil {\n\t\t\t\t\treaderr = io.EOF\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar errMsg string\n\t\t\tdetailed, ok := rpcerror.FromError(readerr)\n\t\t\tif ok {\n\t\t\t\terrMsg = detailed.Error()\n\t\t\t} else {\n\t\t\t\terrMsg = readerr.Error()\n\t\t\t}\n\t\t\t// Fall back to a generic, opaque error.\n\t\t\tif portString == \"\" {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"could not read plugin [%v]: %s\", bin, errMsg)\n\t\t\t}\n\t\t\treturn nil, nil, fmt.Errorf(\"failure reading plugin [%v] (read '%v'): %s\",\n\t\t\t\tbin, portString, errMsg)\n\t\t}\n\t\tif n > 0 && b[0] == '\\n' {\n\t\t\tbreak\n\t\t}\n\t\tportString += string(b[:n])\n\t}\n\t// Parse the output line to ensure it's a numeric port.\n\tvar port int\n\tif port, err = parsePort(portString); err != nil {\n\t\tkillerr := plug.Kill()\n\t\tcontract.IgnoreError(killerr) // ignoring the error because the existing one trumps it.\n\t\treturn nil, nil, fmt.Errorf(\n\t\t\t\"%v plugin [%v] wrote an invalid port to stdout: %w\", prefix, bin, err)\n\t}\n\n\t// After reading the port number, set up a tracer on stdout just so other output doesn't disappear.\n\tstdoutDone := make(chan bool)","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/sdk/go/common/resource/plugin/plugin.go#L398-L434","documentation":"Same handshake-failure path as 'could not read plugin', but this variant fires when a debug/attach port string was captured (portString non-empty, i.e. a debugger was attached or port info existed). It reports what was read from the plugin along with the read error, to aid debugging an attached plugin session.","triggerScenarios":"Run's stdout read fails while an attach/debugger port was configured (attachDebugger path), so the error includes the literal string read from the plugin's stdout.","commonSituations":"Developer attached a debugger to a provider (PULUMI_DEBUG_PLUGINS or --attach-debugger) and the plugin died mid-handshake; garbled output on stdout from debug logging contaminating the port line.","solutions":["Inspect the '(read ...)' payload — it shows exactly what the plugin emitted instead of a clean port.","Retry without debugger attachment to confirm the error is debugger-related.","Check the plugin's stderr for the real crash output.","Ensure plugin code doesn't print logs to stdout (must go to stderr); fix logging in the plugin if you own it.","Reinstall or rebuild the plugin binary."],"exampleFix":"// before: plugin's fmt.Println('starting...') corrupts handshake\nfmt.Println(\"starting provider...\")\n// after\nfmt.Fprintf(os.Stderr, \"starting provider...\\n\")","handlingStrategy":"fallback","validationCode":"// reproduce without debugger attachment first\nunset PULUMI_DEBUG_PLUGINS; pulumi up","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (/failure reading plugin/.test(err.message)) {\n    // inspect the quoted '(read ...)' payload for what the plugin emitted\n  }\n}","preventionTips":["Disable plugin debugger attachment in normal CI runs.","In plugin code, never use fmt.Print* to stdout for logging.","Check for security agents/wrappers intercepting subprocess stdout.","Rebuild plugins with a current Pulumi SDK."],"tags":["go","plugin","handshake","debugger","stdout"],"backgroundTag":"plugin-handshake-failed","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}