{"record":{"id":"0bcc349acad8e9db","repo":"neondatabase/neon","slug":"pg-ctl-status-returned-unexpected-status-code","errorCode":null,"errorMessage":"pg_ctl status returned unexpected status code: {:?}","messagePattern":"pg_ctl status returned unexpected status code: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"control_plane/src/storage_controller.rs","lineNumber":770,"sourceCode":"        Ok(())\n    }\n\n    async fn is_postgres_running(&self) -> anyhow::Result<bool> {\n        let pg_data_path = self.env.base_data_dir.join(\"storage_controller_db\");\n\n        let pg_status_args = [\"-D\", &pg_data_path.to_string_lossy(), \"status\"];\n        let status_exitcode = self.pg_ctl(pg_status_args).await;\n\n        // pg_ctl status returns this exit code if postgres is not running: in this case it is\n        // fine that stop failed.  Otherwise it is an error that stop failed.\n        const PG_STATUS_NOT_RUNNING: i32 = 3;\n        const PG_NO_DATA_DIR: i32 = 4;\n        const PG_STATUS_RUNNING: i32 = 0;\n        match status_exitcode.code() {\n            Some(PG_STATUS_NOT_RUNNING) => Ok(false),\n            Some(PG_NO_DATA_DIR) => Ok(false),\n            Some(PG_STATUS_RUNNING) => Ok(true),\n            Some(code) => Err(anyhow::anyhow!(\n                \"pg_ctl status returned unexpected status code: {:?}\",\n                code\n            )),\n            None => Err(anyhow::anyhow!(\"pg_ctl status returned no status code\")),\n        }\n    }\n\n    fn get_claims_for_path(path: &str) -> anyhow::Result<Option<Claims>> {\n        let category = match path.find('/') {\n            Some(idx) => &path[..idx],\n            None => path,\n        };\n\n        match category {\n            \"status\" | \"ready\" => Ok(None),\n            \"control\" | \"debug\" => Ok(Some(Claims::new(None, Scope::Admin))),\n            \"v1\" => Ok(Some(Claims::new(None, Scope::PageServerApi))),\n            _ => Err(anyhow::anyhow!(\"Failed to determine claims for {}\", path)),","sourceCodeStart":752,"sourceCodeEnd":788,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/control_plane/src/storage_controller.rs#L752-L788","documentation":"storcon classifies pg_ctl status exit codes: 0 = running, 3 = not running, 4 = no data dir. Any other exit code (permissions errors on the data directory, usage errors, differing pg_ctl semantics) is unexpected and reported with this error.","triggerScenarios":"pg_ctl status exits with an out-of-set code: inaccessible or wrongly owned pgdata, an older/newer pg_ctl with different behavior, or a partially deleted data dir that pg_ctl cannot inspect.","commonSituations":"Running as a different user than the owner of storage_controller_db, mixing postgres versions under pg_install, partially deleted data directories.","solutions":["Run pg_ctl -D <base>/storage_controller_db status by hand and read its stderr — it explains the unexpected code","Fix ownership/permissions on the data dir","Rebuild or reinstall the postgres distribution if pg_ctl itself is broken"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let code = pg_ctl([\"-D\", &dir, \"status\"]).await.code();\nmatch code {\n    Some(0) | Some(3) | Some(4) => { /* classified */ }\n    Some(c) => tracing::warn!(\"unexpected pg_ctl status code {c}; run it manually for stderr\"),\n    None => tracing::warn!(\"pg_ctl terminated by a signal\"),\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not chmod/chown the data dir between runs","Pin one postgres distribution per env","Wrap pg_ctl calls to capture stderr so unexpected codes are diagnosable"],"tags":["rust","storage-controller","postgres","pg-ctl","exit-code"],"backgroundTag":"unexpected-exit-code","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}