{"record":{"id":"5eb22f97efc9e9c7","repo":"FuelLabs/fuel-core","slug":"unable-to-produce-blocks-without-a-signer","errorCode":null,"errorMessage":"unable to produce blocks without a signer","messagePattern":"unable to produce blocks without a signer","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/services/consensus_module/poa/src/service.rs","lineNumber":446,"sourceCode":"        // Update last block time\n        self.last_height = height;\n        self.last_timestamp = block_time;\n        self.last_block_created = match self.trigger {\n            Trigger::Open { .. } => deadline.max(last_block_created),\n            _ => last_block_created,\n        };\n\n        Ok(())\n    }\n\n    async fn produce_predefined_block(\n        &mut self,\n        predefined_block: &Block,\n    ) -> anyhow::Result<()> {\n        tracing::info!(\"Producing predefined block\");\n        let last_block_created = Instant::now();\n        if !self.signer.is_available() {\n            return Err(anyhow!(\"unable to produce blocks without a signer\"))\n        }\n\n        // Ask the block producer to create the block\n        let (\n            ExecutionResult {\n                block,\n                skipped_transactions,\n                tx_status,\n                events,\n            },\n            changes,\n        ) = self\n            .block_producer\n            .produce_predefined_block(predefined_block)\n            .await?\n            .into();\n\n        if !skipped_transactions.is_empty() {","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/services/consensus_module/poa/src/service.rs#L428-L464","documentation":"produce_predefined_block — the path that re-executes blocks yielded by the PredefinedBlocks port — checks self.signer.is_available() first, mirroring produce_block. Predefined blocks still need a signature over the produced header, so without a loaded consensus key the predefined-block production loop fails and the node cannot reproduce its scripted chain.","triggerScenarios":"A node configured with predefined blocks (PB: PredefinedBlocks returning blocks, e.g. deterministic test chains) whose signer is SignMode::Unavailable when the predefined block production round starts.","commonSituations":"Test/dev chain configs enabling predefined blocks while overriding consensus_signer to Unavailable; provisioning scripts that start predefined-block nodes before the key is available; forking test setups where the signer field was removed from Config.","solutions":["Set consensus_signer / Config.signer to SignMode::Key(secret) for any node that produces predefined blocks.","Disable the predefined blocks source if this node should not reproduce the scripted chain.","Assert signer.is_available() at startup before the predefined-blocks service begins."],"exampleFix":"// before\nlet config = Config {\n    signer: SignMode::Unavailable,\n    ..Default::default()\n};\n\n// after\nlet config = Config {\n    signer: SignMode::Key(Secret::new(predefined_chain_key.into())),\n    ..Default::default()\n};","handlingStrategy":"validation","validationCode":"// Precondition for any predefined-block node\nuse fuel_core_types::signer::SignMode;\n\nfn assert_predefined_blocks_signable(signer: &SignMode, has_predefined_blocks: bool) -> anyhow::Result<()> {\n    if has_predefined_blocks && !matches!(signer, SignMode::Key(_)) {\n        anyhow::bail!(\"predefined block production requires an available signer\");\n    }\n    Ok(())\n}","typeGuard":"fn can_sign(signer: &SignMode) -> bool {\n    matches!(signer, SignMode::Key(_))\n}","tryCatchPattern":"// Service-level error: log and halt the predefined-blocks flow rather than looping\nif let Err(err) = run_predefined_blocks(&mut task).await {\n    if err.to_string().contains(\"without a signer\") {\n        tracing::error!(\"consensus key missing; predefined-block chain halted\");\n        return Err(err);\n    }\n    return Err(err);\n}","preventionTips":["Provision the consensus key before enabling a predefined-blocks chain.","Assert signer.is_available() in node startup for any config where PredefinedBlocks can yield blocks.","Keep the predefined-chain key in the same provisioning pipeline as other validator secrets."],"tags":["consensus","poa","signer","keys","predefined-blocks","rust"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}