{"record":{"id":"419d951a08d83a3b","repo":"neondatabase/neon","slug":"cannot-refresh-compute-configuration-in-state","errorCode":null,"errorMessage":"Cannot refresh compute configuration in state {:?}","messagePattern":"Cannot refresh compute configuration in state (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"compute_tools/src/compute.rs","lineNumber":2064,"sourceCode":"    // applies it.\n    pub async fn signal_refresh_configuration(&self) -> Result<()> {\n        let states_allowing_configuration_refresh = [\n            ComputeStatus::Running,\n            ComputeStatus::Failed,\n            ComputeStatus::RefreshConfigurationPending,\n        ];\n\n        let mut state = self.state.lock().expect(\"state lock poisoned\");\n        if states_allowing_configuration_refresh.contains(&state.status) {\n            state.status = ComputeStatus::RefreshConfigurationPending;\n            self.state_changed.notify_all();\n            Ok(())\n        } else if state.status == ComputeStatus::Init {\n            // If the compute is in Init state, we can't refresh the configuration immediately,\n            // but we should be able to do that soon.\n            Ok(())\n        } else {\n            Err(anyhow::anyhow!(\n                \"Cannot refresh compute configuration in state {:?}\",\n                state.status\n            ))\n        }\n    }\n\n    // Wrapped this around `pg_ctl reload`, but right now we don't use\n    // `pg_ctl` for start / stop.\n    #[instrument(skip_all)]\n    fn pg_reload_conf(&self) -> Result<()> {\n        let pgctl_bin = Path::new(&self.params.pgbin)\n            .parent()\n            .unwrap()\n            .join(\"pg_ctl\");\n        Command::new(pgctl_bin)\n            .args([\"reload\", \"-D\", &self.params.pgdata])\n            .output()\n            .expect(\"cannot run pg_ctl process\");","sourceCodeStart":2046,"sourceCodeEnd":2082,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/compute_tools/src/compute.rs#L2046-L2082","documentation":"signal_refresh_configuration() only accepts a refresh trigger when the compute is in Running, Failed, or already RefreshConfigurationPending; Init is tolerated (refresh happens later); any other status is rejected with this message. It is a state-machine guard: the HTTP route asked to refresh config while the endpoint is transitioning (e.g. stopping/starting/empty), not a failure of the refresh itself.","triggerScenarios":"Calling signal_refresh_configuration() while state.status is outside {Running, Failed, RefreshConfigurationPending, Init} - e.g. during StopPending/Stopping/Starting or before the first spec is applied.","commonSituations":"Control plane retries a stale configuration-refresh request after the endpoint moved to a transitional status; a race between a stop/restart request and a config update; status left in an unexpected state by an earlier error path.","solutions":["Check the compute status (status endpoint) and wait for Running before requesting a config refresh","If the compute is Failed, a refresh is already allowed - retry and it will be accepted; for other states, let the lifecycle action finish first","If stuck in a transitional status, restart/re-provision the compute and then re-apply configuration"],"exampleFix":"// before\ncompute.signal_refresh_configuration().await?;\n// after\nif !matches!(status, ComputeStatus::Running | ComputeStatus::Failed) {\n    wait_until_status(&compute, |s| s == ComputeStatus::Running).await;\n}\ncompute.signal_refresh_configuration().await?;","handlingStrategy":"validation","validationCode":"// Caller-side gate mirroring the state machine\nconst REFRESHABLE: [ComputeStatus; 3] = [Running, Failed, RefreshConfigurationPending];\nif !REFRESHABLE.contains(&current_status) && current_status != Init {\n    return Ok(()); // skip the call until the compute settles\n}","typeGuard":"fn can_signal_refresh(status: ComputeStatus) -> bool {\n    matches!(status, ComputeStatus::Running | ComputeStatus::Failed | ComputeStatus::RefreshConfigurationPending)\n        || status == ComputeStatus::Init\n}","tryCatchPattern":"// Treat as 'come back later', not as an error worth paging on\nmatch compute.signal_refresh_configuration().await {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"Cannot refresh compute configuration\") => {\n        schedule_retry_after_status_change().await;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Poll compute status and only send config-refresh once Running/Failed","Debounce config updates in the control plane to avoid racing stop/restart","Remember Init is fire-and-forget: the refresh is deferred, so do not re-send immediately"],"tags":["rust","compute-ctl","state-machine","configuration","status","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}