{"record":{"id":"fbff2d6b6b1a91d4","repo":"risingwavelabs/risingwave","slug":"bad-result-from-postgres","errorCode":null,"errorMessage":"bad result from postgres","messagePattern":"bad result from postgres","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/rpc/election/sql.rs","lineNumber":541,"sourceCode":"                    END\n        RETURNING service, id, last_heartbeat;\n        \"#,\n                    table = Self::election_table_name()\n                ),\n                vec![\n                    Value::from(service_name),\n                    Value::from(id),\n                    // special handling for interval\n                    Value::from(ttl.to_string()),\n                ],\n            ))\n            .await?;\n\n        let row = query_result\n            .map(|query_result| ElectionRow::from_query_result(&query_result, \"\"))\n            .transpose()?;\n\n        let row = row.ok_or_else(|| anyhow!(\"bad result from postgres\"))?;\n\n        Ok(row)\n    }\n\n    async fn leader(&self, service_name: &str) -> MetaResult<Option<ElectionRow>> {\n        let query_result = self\n            .conn\n            .query_one(Statement::from_sql_and_values(\n                DatabaseBackend::Postgres,\n                format!(\n                    r#\"SELECT service, id, last_heartbeat FROM {table} WHERE service = $1;\"#,\n                    table = Self::election_table_name()\n                ),\n                vec![Value::from(service_name)],\n            ))\n            .await?;\n\n        let row = query_result","sourceCodeStart":523,"sourceCodeEnd":559,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/rpc/election/sql.rs#L523-L559","documentation":"The PostgreSQL-backed election client's try_campaign expects the campaign INSERT ... ON CONFLICT ... RETURNING query to return exactly one row. An empty result is rejected with 'bad result from postgres', meaning the upsert did not return the leader row as required.","triggerScenarios":"try_campaign on PostgresElectionClient when the RETURNING clause yields no row — e.g. the ON CONFLICT branch matched but no row was returned due to driver/query behavior or store anomalies.","commonSituations":"Postgres version or driver incompatibility changing upsert semantics; concurrent truncation of the election table; connecting through a pooler (e.g. PgBouncer transaction mode) that splits the write/read across sessions or databases.","solutions":["Verify the connection targets the intended Postgres database and schema, and avoid transaction-mode poolers for the election connection.","Check the election table exists with the expected primary key and the Postgres version supports the used upsert syntax.","Look for prior postgres errors in meta logs (deadlock, serialization) and retry election.","If reproducible on a known-good Postgres, file a bug with the campaign query and driver version."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.try_campaign(service, lock_id).await {\n    Err(e) if e.to_string().contains(\"bad result from postgres\") => {\n        tokio::time::sleep(Duration::from_secs(1)).await;\n        // re-check connection target before retrying\n    }\n    other => other?,\n}","preventionTips":["Avoid transaction-mode poolers (PgBouncer) for election connections.","Verify all meta nodes connect to the same database and schema.","Test upsert/RETURNING behavior on your exact Postgres version before deployment."],"tags":["postgres","election","meta","leader-election"],"backgroundTag":"empty-result-set","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}