{"id":"eff5be248ec8d84e","repo":"jackc/pgx","slug":"invalid-scram-server-first-message-received-from-s-eff5be","errorCode":null,"errorMessage":"invalid SCRAM server-first-message received from server: did not include s=","messagePattern":"invalid SCRAM server-first-message received from server: did not include s=","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgconn/auth_scram.go","lineNumber":254,"sourceCode":"\t\tsc.clientGS2Header = []byte(\"y,,\")\n\tdefault:\n\t\tsc.clientGS2Header = []byte(\"n,,\")\n\t}\n\n\treturn append(sc.clientGS2Header, sc.clientFirstMessageBare...)\n}\n\nfunc (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {\n\tsc.serverFirstMessage = serverFirstMessage\n\tbuf := serverFirstMessage\n\tif !bytes.HasPrefix(buf, []byte(\"r=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include r=\")\n\t}\n\tbuf = buf[2:]\n\n\tidx := bytes.IndexByte(buf, ',')\n\tif idx == -1 {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include s=\")\n\t}\n\tsc.clientAndServerNonce = buf[:idx]\n\tbuf = buf[idx+1:]\n\n\tif !bytes.HasPrefix(buf, []byte(\"s=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include s=\")\n\t}\n\tbuf = buf[2:]\n\n\tidx = bytes.IndexByte(buf, ',')\n\tif idx == -1 {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include i=\")\n\t}\n\tsaltStr := buf[:idx]\n\tbuf = buf[idx+1:]\n\n\tif !bytes.HasPrefix(buf, []byte(\"i=\")) {\n\t\treturn errors.New(\"invalid SCRAM server-first-message received from server: did not include i=\")","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgconn/auth_scram.go#L236-L272","documentation":"Returned by recvServerFirstMessage when the server-first-message has 'r=<nonce>' but no comma separator follows, so the parser cannot find the required 's=' (salt) attribute. RFC 5802 requires the message to contain r=, then s=, then i=, comma-separated.","triggerScenarios":"Server sends a server-first-message like 'r=xyz' with no trailing comma/salt. Same class as a truncated/malformed SASL message from a non-conformant server or a tampering intermediary.","commonSituations":"Truncated network reads surfaced as a complete message by a buggy proxy; non-conformant server implementations; MITM tampering.","solutions":["Verify the endpoint is genuine PostgreSQL and not a malformed proxy target.","Eliminate any SASL-rewriting middleware between client and server.","Inspect the raw AuthenticationSASLContinue payload to confirm truncation, then report to the server/proxy vendor."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := connect(); err != nil && strings.Contains(err.Error(), \"invalid SCRAM server-first-message\") {\n    // Treat as a non-retryable protocol/tamper condition; alert ops.\n    return status.Errorf(codes.Unauthenticated, \"auth exchange corrupted: %v\", err)\n}","preventionTips":["Ensure end-to-end TLS with certificate verification.","Remove SASL-rewriting middleware.","Capture a packet trace if it recurs to localize the corruption source."],"tags":["authentication","scram","protocol","security"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}