{"record":{"id":"cc4a3cc4be828a73","repo":"dotnet/orleans","slug":"read-a-reminder-entry-for-wrong-service-id-read","errorCode":null,"errorMessage":"Read a reminder entry for wrong Service id. Read {tableEntry}, but my service id is {serviceIdStr}. Going to discard it.","messagePattern":"Read a reminder entry for wrong Service id\\. Read (.+?), but my service id is (.+?)\\. Going to discard it\\.","errorType":"exception","errorClass":"OrleansException","httpStatus":null,"severity":"error","filePath":"src/Azure/Orleans.Reminders.AzureStorage/Storage/AzureBasedReminderTable.cs","lineNumber":122,"sourceCode":"                    GrainId = GrainId.Parse(tableEntry.GrainReference!),\n                    ReminderName = tableEntry.ReminderName!,\n                    StartAt = LogFormatter.ParseDate(tableEntry.StartAt!),\n                    Period = TimeSpan.Parse(tableEntry.Period!),\n                    ETag = eTag,\n                };\n            }\n            catch (Exception exc)\n            {\n                LogErrorParsingReminderEntry(exc, tableEntry);\n                throw;\n            }\n            finally\n            {\n                string serviceIdStr = this.clusterOptions.ServiceId;\n                if (!tableEntry.ServiceId!.Equals(serviceIdStr))\n                {\n                    LogWarningAzureTable_ReadWrongReminder(tableEntry, serviceIdStr);\n                    throw new OrleansException($\"Read a reminder entry for wrong Service id. Read {tableEntry}, but my service id is {serviceIdStr}. Going to discard it.\");\n                }\n            }\n        }\n\n        private static ReminderTableEntry ConvertToTableEntry(ReminderEntry remEntry, string serviceId, string deploymentId)\n        {\n            string partitionKey = ReminderTableEntry.ConstructPartitionKey(serviceId, remEntry.GrainId);\n            string rowKey = ReminderTableEntry.ConstructRowKey(remEntry.GrainId, remEntry.ReminderName);\n\n            var consistentHash = remEntry.GrainId.GetUniformHashCode();\n\n            return new ReminderTableEntry\n            {\n                PartitionKey = partitionKey,\n                RowKey = rowKey,\n\n                ServiceId = serviceId,\n                DeploymentId = deploymentId,","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Azure/Orleans.Reminders.AzureStorage/Storage/AzureBasedReminderTable.cs#L104-L140","documentation":"Thrown by AzureBasedReminderTable when a reminder entry read from Azure Table Storage has a ServiceId that does not match the current silo's cluster ServiceId. Each Orleans cluster has a unique ServiceId (from ClusterOptions); reminder entries are scoped to that ServiceId. Reading a reminder from a different service indicates data contamination or a configuration mismatch, and the entry is discarded.","triggerScenarios":"During ReadRow/ReadRows, after parsing a ReminderTableEntry from the table, the finally block checks tableEntry.ServiceId against clusterOptions.ServiceId. If they differ, it throws OrleansException. This can happen if the Azure Table contains reminder data from a different cluster or if the ServiceId changed between deployments.","commonSituations":"ServiceId in ClusterOptions changed between deployments but the same Azure Table is reused — old reminders from the previous ServiceId are still present. Two clusters accidentally pointed at the same Azure Storage account/table. Copy-paste of config that overwrote the ServiceId. Environment promotion without updating the ServiceId.","solutions":["Ensure ClusterOptions.ServiceId is stable and unique per logical service across deployments — do not change it between restarts.","If the ServiceId was intentionally changed, clean up old reminder entries from the Azure Table or use a different table.","Use separate Azure Storage accounts or table names per cluster/environment to avoid cross-contamination.","Audit the Azure Table for entries with mismatched ServiceId values and delete stale rows."],"exampleFix":"// before: ServiceId changed between deployments\nbuilder.Configure<ClusterOptions>(o => o.ServiceId = Guid.NewGuid().ToString()); // new each run!\n\n// after: stable ServiceId\nbuilder.Configure<ClusterOptions>(o =>\n{\n    o.ServiceId = \"my-service-prod\"; // stable across restarts\n    o.ClusterId = \"cluster-1\";\n});","handlingStrategy":"validation","validationCode":"// Verify ServiceId is stable before starting the silo\nvar serviceId = configuration[\"Orleans:ClusterOptions:ServiceId\"];\nif (string.IsNullOrWhiteSpace(serviceId) || serviceId == Guid.NewGuid().ToString())\n    throw new InvalidOperationException(\"ClusterOptions.ServiceId must be a stable, unique value — do not randomize it.\");","typeGuard":null,"tryCatchPattern":"try { await reminderTable.ReadRows(begin, end); }\ncatch (OrleansException ex) when (ex.Message.Contains(\"wrong Service id\"))\n{\n    logger.LogCritical(ex, \"Reminder table contains entries from a different ServiceId — clean up the table or fix the ServiceId.\");\n    throw;\n}","preventionTips":["Never randomize ClusterOptions.ServiceId — it must be stable across restarts.","Use separate Azure Storage tables or accounts per environment to avoid reminder contamination.","When changing ServiceId intentionally, clean up stale reminder entries from the old ServiceId.","Document the ServiceId in deployment config and audit it during environment promotion."],"tags":["azure","table-storage","reminders","service-id","configuration","data-contamination","orleans"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}