ruvnet/ruflo · error
Event must have a valid aggregateId string
Error message
Event must have a valid aggregateId string
What it means
RvfEventLog.append() validates the incoming DomainEvent before persisting and requires aggregateId to be a non-empty string, because it keys per-aggregate versioning (the aggregateVersions map) and the on-disk index on that field. Events missing, blanking, or carrying a non-string aggregateId are rejected before any write, keeping the log consistent. The usual culprit is an event assembled from spreads or deserialized JSON where the field was dropped or renamed.
Source
Thrown at v3/@claude-flow/shared/src/events/rvf-event-log.ts:163
this.events = [];
this.aggregateIndex.clear();
this.aggregateVersions.clear();
this.snapshots.clear();
this.initialized = false;
this.emit('shutdown');
}
// ===========================================================================
// Write Operations
// ===========================================================================
/** Append a domain event to the log. */
async append(event: DomainEvent): Promise<void> {
this.ensureInitialized();
if (!event.aggregateId || typeof event.aggregateId !== 'string') {
throw new Error('Event must have a valid aggregateId string');
}
if (!event.type || typeof event.type !== 'string') {
throw new Error('Event must have a valid type string');
}
// Assign next version for aggregate
const currentVersion = this.aggregateVersions.get(event.aggregateId) ?? 0;
const nextVersion = currentVersion + 1;
event.version = nextVersion;
// Persist to disk first (crash-safe ordering)
this.appendRecord(this.config.logPath, event);
// Update in-memory state
this.indexEvent(event);
this.emit('event:appended', event);
View on GitHub (pinned to fa13ee4ad6)
Solutions
- Set aggregateId explicitly at every append site — String(aggregate.id) if the id is numeric
- Fix serialization so the field arrives named exactly aggregateId
- Centralize event construction in a helper that validates the DomainEvent shape once
Example fix
// before
await log.append({ type: 'order.updated', payload } as DomainEvent); // throws
// after
await log.append({ aggregateId: String(order.id), type: 'order.updated', payload } as DomainEvent); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof event.aggregateId !== 'string' || event.aggregateId.length === 0) {
throw new TypeError('DomainEvent.aggregateId must be a non-empty string');
}
await log.append(event); Type guard
function isDomainEvent(e: unknown): e is DomainEvent {
return !!e && typeof e === 'object'
&& typeof (e as any).aggregateId === 'string' && (e as any).aggregateId.length > 0
&& typeof (e as any).type === 'string' && (e as any).type.length > 0;
} Try / catch
try {
await log.append(event);
} catch (e) {
if (e instanceof Error && e.message.includes('aggregateId')) {
// fix the producer: the event was missing/blank/non-string aggregateId
} else throw e;
} Prevention
- Construct events through one validated factory instead of ad-hoc object literals
- Run the isDomainEvent guard before append() at integration boundaries
- String() numeric ids at the call site: String(order.id)
When it happens
Trigger: append({ type: 'task.created', payload }) with no aggregateId; aggregateId: '' or undefined; aggregateId supplied as a number (e.g. 42) or any non-string value.
Common situations: Events built via { ...base, type } that accidentally drop the id; JSON deserialized with a field-name mismatch (aggregate_id vs aggregateId); test fixtures missing the field; ORM rows used directly as events.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Event must have a valid type string
- [RvfEventLog] Invalid file header in ${filePath}
- RvfEventLog not initialized. Call initialize() first.
- localCompute: no adapter for graphId=${input.graphId}
- Invalid completion type
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/f6222c8e422d3b89.
Report an issue: GitHub.