ruvnet/ruflo · error · Error
records must be a non-empty array of
Error message
records must be a non-empty array of {id?, vector, text?} What it means
Thrown by the agenticow_ingest handler (agenticow-tools.ts:128) when the records argument is not a non-empty array of {id?, vector, text?}. Ingestion is the only way to add vectors to an agenticow memory file, and the tool refuses empty batches so a call cannot succeed while silently doing nothing.
Solutions
- Wrap a single record in an array: records: [{ vector: [0.1, 0.2], text: 'item' }]
- Skip the ingest call when your batch logic produced zero records
- Log batch size before calling so empty batches are visible in traces
Example fix
// before
await callTool('agenticow_ingest', { path: p, records: [] });
// throws: records must be a non-empty array of {id?, vector, text?}
// after
if (batch.length > 0) {
await callTool('agenticow_ingest', { path: p, dimension: 384, records: batch });
} Defensive patterns
Strategy: validation
Validate before calling
if (!Array.isArray(records) || records.length === 0) {
// nothing to ingest — skip the call instead of erroring
return { skipped: true, reason: 'empty batch' };
} Type guard
const isNonEmptyArray = (v: unknown): v is unknown[] => Array.isArray(v) && v.length > 0;
Prevention
- Guard ingest calls with a batch-length check so empty batches short-circuit locally
- Wrap single records in an array at the call site
When it happens
Trigger: Calling agenticow_ingest with records omitted, records: [], or a single record object passed bare instead of wrapped in an array (records: {vector: [...]} instead of records: [{vector: [...]}]).
Common situations: A dedupe or threshold filter upstream emptied the batch before ingest; JSON pipelines that unwrap one-element arrays; a first-call seed script whose data load returned nothing.
Related errors
- at least one candidate is required
- candidate must ingest at least one vector
- label exceeds 256 chars
- label is required
- label may only contain [A-Za-z0-9_.\\-:/@]
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/91a4c3cb994fa623.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/mcp-tools/agenticow-tools.ts:128
id: { type: 'integer', description: 'Explicit id (auto-assigned when omitted)' },
vector: { type: 'array', items: { type: 'number' }, description: 'Embedding vector (length must equal the memory dimension)' },
text: { type: 'string', description: 'Optional payload surfaced on query hits' },
},
required: ['vector'],
},
},
dimension: { type: 'integer', description: 'Vector dimension (required only when path does not exist yet)' },
},
required: ['path', 'records'],
},
handler: async (input) => {
const api = await loadAgenticow();
if (!api) return degradedResult('agenticow-not-found');
const path = resolveMemoryPath(String(input.path));
const records = input.records as Array<{ id?: number; vector: number[]; text?: string }>;
if (!Array.isArray(records) || records.length === 0) {
throw new Error('records must be a non-empty array of {id?, vector, text?}');
}
for (const r of records) {
if (!Array.isArray(r.vector) || r.vector.length === 0) {
throw new Error('each record requires a non-empty numeric vector');
}
}
const dim = (input.dimension as number | undefined) ?? records[0].vector.length;
const mem = await openWithLineage(api, path, dim);
try {
const result = await mem.ingest(records.map((r) => ({
...(typeof r.id === 'number' ? { id: r.id } : {}),
vector: r.vector,
...(r.text !== undefined ? { text: r.text } : {}),
})));
await mem.save?.(manifestFor(path));
return { success: true, path, ingested: result };
} finally {
await mem.close?.();View on GitHub (pinned to fa13ee4ad6)