---
title: Reactor identity and renaming
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/reactors/identity.mdx
description: Preserve a reactor's processing position during refactoring and remove abandoned observers safely.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


A reactor's id identifies its observer and processing position. By default, the id is the full type name, including its namespace. Moving a reactor to another namespace or renaming its class therefore creates a different observer unless you preserve the id explicitly.

## Keep identity stable

For a side-effecting reactor, choose an explicit id before its first deployment. That lets you reorganize code without changing its processing identity. This declaration excerpt leaves your existing handlers unchanged:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Reactors;

// For an already deployed reactor, use its existing stored observer id here.
[Reactor("MyApp.Orders.SendWelcomeEmail")]
public class SendWelcomeEmail : IReactor
{
    // Keep the existing event handlers unchanged.
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/reactors/identity/explicit-id.md)

</TabItem>
</Tabs>

For a reactor that is already deployed, **use its current stored id**, not a new friendly name. If its default id was `MyApp.Orders.SendWelcomeEmail`, pin that exact value before moving or renaming the class. Adding an explicit id with a different value is itself an identity change.

## Why side effects run again

A new observer catches up from the beginning of the event sequence as ordinary observation, not replay. Its handlers can send every historical notification or repeat every external write. `OnceOnly` excludes replay; it does not suppress this initial catch-up. See [What OnceOnly guarantees](/chronicle/reactors/once-only/#what-it-does-and-does-not-guarantee).

Preserving the old id keeps its processing position. It does not provide exactly-once execution: recovery can still re-deliver an event, so keep side effects [idempotent](/chronicle/reactors/delivery-identity/).

## Find and remove abandoned observers

Use the Workbench's **Observers** page or `cratis chronicle observers list` to compare stored ids with the ids your application declares. Inspect the old observer before deleting anything: another service or namespace may still use it.

If the identity change was accidental, restore the old id in the reactor declaration. Removing the old observer does **not** transfer its position to the new one or undo side effects the new one has already performed.

If the old observer is genuinely abandoned, stop all clients that subscribe to it, then remove it by its stored id:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
var result = await eventStore.Observers.Remove("the-observer");
if (!result.IsRemoved)
{
    Console.WriteLine($"Not removed: {result.Outcome} in namespace {result.BlockingNamespace}");
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/concepts/observers/remove.md)

</TabItem>
</Tabs>

Removal affects the whole event store, across namespaces, and refuses while the observer is running or subscribed anywhere. See [Removing an observer](/chronicle/concepts/observers/#removing-an-observer).

The [.NET discovery diagnostics](/chronicle/clients/dotnet/discovery-report/) report likely identity changes before registration. Treat findings as evidence to investigate, not proof: a rolling deployment can hide a rename while the old instance is still subscribed.
