nexu-io/open-design · error
ingestion body is required
Error message
ingestion body is required
What it means
Thrown by ingestAutomationSource() when the input argument is falsy or not an object. It is the top-level body-presence guard that runs before any field is read; subsequent field errors (sourceKind, bodyMarkdown) cannot fire until this passes.
Source
Thrown at apps/daemon/src/automation-ingestions.ts:370
packet: AutomationContentPacket,
): Promise<void> {
const packets = await listAutomationSourcePackets(dataDir);
const next = packets.filter((existing) => existing.id !== packet.id);
next.push(packet);
await writePackets(dataDir, next);
}
function jsonObjectFrom(value: unknown): Record<string, JsonValue> {
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
return value as Record<string, JsonValue>;
}
export async function ingestAutomationSource(
dataDir: string,
input: CreateAutomationSourceIngestionRequest,
): Promise<AutomationSourceIngestionResponse> {
if (!input || typeof input !== 'object') {
throw new Error('ingestion body is required');
}
const sourceKind = sourceKindFrom(input.sourceKind);
const bodyMarkdown = typeof input.bodyMarkdown === 'string' ? input.bodyMarkdown.trim() : '';
if (!bodyMarkdown) throw new Error('bodyMarkdown is required');
const template = input.templateId ? await getAnyAutomationTemplate(dataDir, input.templateId) : null;
const templateSinks = template?.outputSinks ?? ['memory'];
const candidateSinks = outputSinksFrom(input.candidateSinks, templateSinks);
const reviewPolicy = reviewPolicyFrom(input.reviewPolicy, template?.reviewPolicy ?? 'always');
const tokenCompression = compressionModeFrom(
input.tokenCompression,
template?.tokenCompression ?? 'balanced',
);
const packetId = `packet_${randomUUID()}`;
const sourceEventId = `source_event_${randomUUID()}`;
const capturedAt = new Date().toISOString();
const sourceRef =
optionalString(input.sourceRef) ??View on GitHub (pinned to 5be4028344)
Solutions
- Ensure the HTTP layer parses JSON bodies before invoking ingestAutomationSource.
- Pass a parsed object literal, not a JSON string.
- Confirm the client sends Content-Type: application/json with an object body.
Example fix
// before
await ingestAutomationSource(dataDir, JSON.stringify({ sourceKind: 'upload', bodyMarkdown: '...' }));
// after
await ingestAutomationSource(dataDir, { sourceKind: 'upload', bodyMarkdown: '...' }); Defensive patterns
Strategy: type-guard
Validate before calling
if (!input || typeof input !== 'object') {
throw new Error('ingestion body is required');
} Type guard
function isIngestionBody(value: unknown): value is Record<string, unknown> {
return !!value && typeof value === 'object' && !Array.isArray(value);
} Prevention
- Apply express.json() (or equivalent) middleware so req.body is a parsed object.
- Pass parsed objects to ingestAutomationSource; never a JSON string.
- Confirm Content-Type: application/json on the client.
When it happens
Trigger: Calling ingestAutomationSource() (or POSTing to the ingestion endpoint) with null, undefined, a primitive, or a string body that was not parsed into an object. Also reached if an upstream handler forgot to JSON.parse the request body.
Common situations: Express route missing express.json() middleware so req.body is undefined; client sending a plain-text body with the wrong content-type; programmatic caller passing a raw JSON string instead of a parsed object.
Related errors
- sourceKind must be one of upload, url, repo, connector, arti
- bodyMarkdown is required
- proposal body is required
- proposal title is required
- proposal summary is required
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/78199e1b806a224c.
Report an issue: GitHub.