nexu-io/open-design · error
bodyMarkdown is required
Error message
bodyMarkdown is required
What it means
Thrown by ingestAutomationSource() after sourceKind is validated, when bodyMarkdown is not a non-empty string after trimming. bodyMarkdown is the actual content being ingested, so an empty value leaves nothing to packetize or propose.
Source
Thrown at apps/daemon/src/automation-ingestions.ts:374
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) ??
optionalString(input.connectorId) ??
optionalString(input.artifactId) ??
optionalString(input.conversationId) ??
sourceKind;View on GitHub (pinned to 5be4028344)
Solutions
- Supply a non-empty bodyMarkdown string with the real content to ingest.
- If content is genuinely empty, skip the ingestion call rather than sending an empty body.
- Validate on the client side that bodyMarkdown.trim() is non-empty before posting.
Example fix
// before
await ingestAutomationSource(dataDir, { sourceKind: 'upload', bodyMarkdown: '' });
// after
await ingestAutomationSource(dataDir, { sourceKind: 'upload', bodyMarkdown: '# Notes\n\nReal content here.' }); Defensive patterns
Strategy: validation
Validate before calling
if (typeof input.bodyMarkdown !== 'string' || !input.bodyMarkdown.trim()) {
throw new Error('bodyMarkdown is required');
} Type guard
function hasNonEmptyBodyMarkdown(value: unknown): value is string {
return typeof value === 'string' && value.trim().length > 0;
} Prevention
- Disable the submit action until bodyMarkdown has non-whitespace content.
- Validate sourceKind first so its error does not mask this one.
- Skip the call entirely when there is no content to ingest.
When it happens
Trigger: POST to the ingestion endpoint with bodyMarkdown omitted, set to an empty string, whitespace-only, or a non-string type. The check runs after sourceKind, so a bad sourceKind masks this error.
Common situations: Client attaching metadata but forgetting the content payload; form submitted before the user typed anything; upstream extractor returned an empty string for the page content.
Related errors
- sourceKind must be one of upload, url, repo, connector, arti
- ingestion body 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/0a1daed75cd94411.
Report an issue: GitHub.