{"record":{"id":"b915f760ec395a4f","repo":"risingwavelabs/risingwave","slug":"subscription-on-table-has-invalid-retention-sec","errorCode":null,"errorMessage":"subscription on table {} has invalid retention seconds {}","messagePattern":"subscription on table (.+?) has invalid retention seconds (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/controller/streaming_job.rs","lineNumber":259,"sourceCode":"impl CatalogController {\n    pub async fn get_table_change_log_truncate_info(\n        &self,\n    ) -> MetaResult<TableChangeLogTruncateInfo> {\n        let inner = self.inner.read().await;\n\n        let subscriptions: Vec<(TableId, i64)> = Subscription::find()\n            .select_only()\n            .columns([\n                subscription::Column::DependentTableId,\n                subscription::Column::RetentionSeconds,\n            ])\n            .into_tuple()\n            .all(&inner.db)\n            .await?;\n        let mut subscription_retention_seconds = HashMap::new();\n        for (table_id, retention_seconds) in subscriptions {\n            let retention_seconds = u64::try_from(retention_seconds).map_err(|_| {\n                anyhow!(\n                    \"subscription on table {} has invalid retention seconds {}\",\n                    table_id,\n                    retention_seconds\n                )\n            })?;\n            subscription_retention_seconds\n                .entry(table_id)\n                .and_modify(|retention: &mut u64| *retention = (*retention).max(retention_seconds))\n                .or_insert(retention_seconds);\n        }\n\n        let jobs: Vec<JobId> = StreamingJobModel::find()\n            .select_only()\n            .column(streaming_job::Column::JobId)\n            .filter(\n                Condition::any()\n                    .add(streaming_job::Column::JobStatus.eq(JobStatus::Creating))\n                    .add(streaming_job::Column::RefreshIntervalSec.is_not_null()),","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/controller/streaming_job.rs#L241-L277","documentation":"get_table_change_log_truncate_info reads subscription retention_seconds, stored as i64, from the subscription catalog and converts each to u64. A negative or overflowing value cannot convert, producing this error naming the table and raw value.","triggerScenarios":"Any subscription row whose retention_seconds is negative or exceeds u64::MAX — e.g. a corrupted or manually edited catalog value, a sentinel like i64::MIN/-1 written by a buggy code path, or deserialized protobuf defaults written as -1.","commonSituations":"Manual SQL fixes on the catalog; a version where -1 encoded 'infinite retention' before the format changed; interrupted CREATE SUBSCRIPTION writing a partial value.","solutions":["Query the subscription table for the listed table_id and correct the negative retention_seconds to a valid non-negative value.","Drop and re-create the subscription with a valid retention setting if the row is corrupted.","Check whether any code path writes -1 or i64 sentinel values as retention and fix the writer.","Restart the meta node after repair so truncate info computation retries."],"exampleFix":"// before\n-- catalog row: retention_seconds = -1\nUPDATE subscription SET retention_seconds = 86400 WHERE dependent_table_id = <table_id>;\n// after\n-- or via SQL: drop and recreate with valid retention\nDROP SUBSCRIPTION sub;\nCREATE SUBSCRIPTION sub FROM mv WITH (retention_seconds = 86400);","handlingStrategy":"validation","validationCode":"SELECT dependent_table_id, retention_seconds FROM subscription WHERE retention_seconds < 0;\n-- must return zero rows before triggering truncate-info computation","typeGuard":"fn valid_retention(v: i64) -> Option<u64> { u64::try_from(v).ok() }","tryCatchPattern":"match u64::try_from(retention_seconds) {\n    Ok(v) => map.insert(table_id, v),\n    Err(_) => {\n        warn!(\"skipping subscription on table {table_id}: invalid retention {retention_seconds}\");\n        // repair the row before re-running truncate info\n    }\n}","preventionTips":["Never hand-edit catalog numeric columns","Audit code paths for -1 sentinel retention values","After upgrades, scan subscription rows for negative retention","Re-create corrupted subscriptions instead of patching values in place"],"tags":["subscription","retention","catalog","type-conversion"],"backgroundTag":"value-out-of-range","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}