{"record":{"id":"83c290af70ed7563","repo":"neondatabase/neon","slug":"setting-scheduling-policy-unsuccessful-for-safekee","errorCode":null,"errorMessage":"setting scheduling policy unsuccessful for safekeeper {node_id}: {}","messagePattern":"setting scheduling policy unsuccessful for safekeeper (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"control_plane/src/storage_controller.rs","lineNumber":927,"sourceCode":"\n        Ok(response.generation)\n    }\n\n    #[instrument(skip(self))]\n    pub async fn upsert_safekeeper(\n        &self,\n        node_id: NodeId,\n        request: serde_json::Value,\n    ) -> anyhow::Result<()> {\n        let resp = self\n            .dispatch_inner::<serde_json::Value>(\n                Method::POST,\n                format!(\"control/v1/safekeeper/{node_id}\"),\n                Some(request),\n            )\n            .await?;\n        if !resp.status().is_success() {\n            anyhow::bail!(\n                \"setting scheduling policy unsuccessful for safekeeper {node_id}: {}\",\n                resp.status()\n            );\n        }\n        Ok(())\n    }\n\n    #[instrument(skip(self))]\n    pub async fn safekeeper_scheduling_policy(\n        &self,\n        node_id: NodeId,\n        scheduling_policy: SkSchedulingPolicy,\n    ) -> anyhow::Result<()> {\n        self.dispatch::<SafekeeperSchedulingPolicyRequest, ()>(\n            Method::POST,\n            format!(\"control/v1/safekeeper/{node_id}/scheduling_policy\"),\n            Some(SafekeeperSchedulingPolicyRequest { scheduling_policy }),\n        )","sourceCodeStart":909,"sourceCodeEnd":945,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/control_plane/src/storage_controller.rs#L909-L945","documentation":"storcon_cli / the control plane asks the storage controller to change a safekeeper's scheduling policy via POST control/v1/safekeeper/{node_id}. Any non-2xx HTTP response is reported with the node id and status code; the response body is not included in the message.","triggerScenarios":"The node id is not registered in storcon's database (404), the policy payload is invalid (400), auth is required but the client is unauthenticated (401), or storcon hit an internal error (500) applying the policy.","commonSituations":"Passing a safekeeper node id that was never registered, a stale storcon database after re-adding safekeepers, auth enabled on storcon while the CLI lacks a token.","solutions":["List registered safekeepers via the storcon CLI and confirm the node id exists","Check the storage controller's logs for the server-side reason behind the status code","Authenticate the CLI if storcon requires JWT","Validate the scheduling policy value before sending"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"let registered: Vec<NodeId> = storcon.list_safekeepers().await?;\nanyhow::ensure!(registered.contains(&node_id), \"node {node_id} not registered in storcon\");","typeGuard":null,"tryCatchPattern":"let resp = dispatch(Method::POST, format!(\"control/v1/safekeeper/{node_id}\"), Some(req)).await?;\nmatch resp.status() {\n    s if s.is_success() => {}\n    StatusCode::NOT_FOUND => { /* node unknown: list safekeepers, re-register */ }\n    StatusCode::UNAUTHORIZED => { /* obtain/refresh the JWT */ }\n    s => { /* read the response body for details; check storcon logs */ }\n}","preventionTips":["Verify node ids with a list call before mutating them","Log the response body, not just the status code","Keep the CLI and the storage controller on the same version"],"tags":["rust","storage-controller","safekeeper","http","api","storcon-cli"],"backgroundTag":"http-error-response","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}