{"record":{"id":"602604b9d190675d","repo":"risingwavelabs/risingwave","slug":"adhoc-recovery-triggered","errorCode":null,"errorMessage":"adhoc recovery triggered","messagePattern":"adhoc recovery triggered","errorType":"error_code","errorClass":"MetaError","httpStatus":null,"severity":"info","filePath":"src/meta/src/error.rs","lineNumber":132,"sourceCode":"        ConnectorError,\n    ),\n\n    #[error(\"Sink error: {0}\")]\n    Sink(\n        #[from]\n        #[backtrace]\n        SinkError,\n    ),\n\n    #[error(transparent)]\n    Internal(\n        #[from]\n        #[backtrace]\n        anyhow::Error,\n    ),\n\n    // Indicates that recovery was triggered manually.\n    #[error(\"adhoc recovery triggered\")]\n    AdhocRecovery,\n\n    #[error(\"Integrity check failed\")]\n    IntegrityCheckFailed,\n\n    #[error(\"{0} has been deprecated, please use {1} instead.\")]\n    Deprecated(String, String),\n\n    #[error(transparent)]\n    NotImplemented(#[from] NotImplemented),\n\n    #[error(\"Secret error: {0}\")]\n    SecretError(\n        #[from]\n        #[backtrace]\n        SecretError,\n    ),\n}","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/error.rs#L114-L150","documentation":"MetaError::AdhocRecovery signals that recovery of stream fragments/jobs was triggered manually rather than by automatic failure detection. It is thrown by the meta recovery machinery when an operator explicitly requests recovery (e.g. via a catalog manager or admin operation). It is a control-flow signal, not a fault: its message has no fields and it exists so callers can distinguish manual recovery requests from real errors.","triggerScenarios":"An explicit ad-hoc recovery call: e.g. `recover_driver.adhoc_recovery()` or manual recovery triggered from cluster management operations (such as the catalog handler `metadata_manager`/stream manager requesting recovery when something looks stale).","commonSituations":"Administrators manually trigger recovery after noticing stuck stream jobs or after applying hot-reload/config changes; operator tooling calls recovery to reschedule fragments after a transient glitch.","solutions":["Treat this variant as an expected control signal: match it separately and return success once recovery completes","Ensure only one adhoc recovery runs at a time; concurrent triggers may log noise","Check recovery status afterwards (streaming job states) rather than retrying","If this fires unintentionally, audit the code path that calls manual recovery"],"exampleFix":"// before\nmeta_recovery_err.map_err(to_grpc)?;\n// after\nmatch meta_recovery_err {\n    MetaError::AdhocRecovery => Ok(recovery_completed_response()),\n    other => Err(to_grpc(other)),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_adhoc_recovery(e: &MetaError) -> bool {\n    matches!(e, MetaError::AdhocRecovery)\n}","tryCatchPattern":"match recovery_result {\n    Err(MetaError::AdhocRecovery) => info!(\"manual recovery requested; not an error\"),\n    Err(e) => return Err(e.into()),\n    Ok(v) => return Ok(v),\n}","preventionTips":["Treat this variant as a control-flow signal, never retry it as a failure","Serialize manual recovery triggers behind a mutex/leader to avoid concurrent runs","Document the manual-recovery entry point for operators"],"tags":["recovery","meta-node","control-flow"],"backgroundTag":"invalid-state-transition","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"}