---
title: 'CHR0030: [ChildrenFrom] child collection property auto-maps to nothing'
---

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

## Rule Description

A collection property on a `[ChildrenFrom]` child is filled by AutoMap, which matches the event's property of the same name. When no source event carries a matching property and nothing maps it explicitly, the property silently projects as an empty collection. Rename the property to match the event, or bridge the names with `[SetFrom<TEvent>(nameof(...))]`.

## Severity

Warning

## Example

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

```csharp
using Cratis.Chronicle.Events;
using Cratis.Chronicle.Keys;
using Cratis.Chronicle.Projections.ModelBound;

public record Chr0033Note(string Text);

[EventType]
public record Chr0033LineAdded(string LineNumber, string Description, IReadOnlyList<Chr0033Note> Annotations);

public record Chr0033Line(
    [Key] string LineNumber,
    string Description,
    IReadOnlyList<Chr0033Note> Notes);

public record Chr0033Order(
    [Key] Guid Id,

    // Warning CHR0030: the child property 'Notes' matches no property on Chr0033LineAdded (the event
    // carries 'Annotations'), so AutoMap fills it from nothing and it always projects as empty.
    [ChildrenFrom<Chr0033LineAdded>(key: nameof(Chr0033LineAdded.LineNumber))]
    IEnumerable<Chr0033Line> Lines);
```

</TabItem>
</Tabs>

## Why This Rule Exists

A `[ChildrenFrom]` child is projected from its own event, and each of its properties is filled by AutoMap — Chronicle copies the event property of the same name onto it. When the child declares a collection property whose name matches **no** property on that event, and no `[SetFrom]` (or other mapping attribute) bridges the gap, there is nothing for AutoMap to copy. The child still projects, but that collection is always empty — a silent data loss rather than a compile or runtime error.

There are two fixes, depending on which name is right:

- **Rename the property to match the event** — call the child property what the event calls it, and AutoMap wires it automatically.
- **Bridge the names explicitly** — keep the property name and point it at the event property with `[SetFrom<TEvent>(nameof(TEvent.Property))]`.

This is the child-collection mirror of the scalar mapping-source rules: the same empty-projection trap the runtime projection engine also reports as a warning, surfaced here at build time so it never reaches production data.

## Related Rules

- **CHR0024** — the scalar version: a read model property with no mapping source that AutoMap cannot populate.
- **CHR0025** — the inverse collision: an explicitly sourced property that AutoMap *overwrites* from another event.
- [Model-bound projections](/chronicle/projections/model-bound/) — the full reference.
