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

  1. Ensure the HTTP layer parses JSON bodies before invoking ingestAutomationSource.
  2. Pass a parsed object literal, not a JSON string.
  3. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/78199e1b806a224c. Report an issue: GitHub.