{"record":{"id":"0a1daed75cd94411","repo":"nexu-io/open-design","slug":"bodymarkdown-is-required","errorCode":null,"errorMessage":"bodyMarkdown is required","messagePattern":"bodyMarkdown is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/automation-ingestions.ts","lineNumber":374,"sourceCode":"  next.push(packet);\n  await writePackets(dataDir, next);\n}\n\nfunction jsonObjectFrom(value: unknown): Record<string, JsonValue> {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) return {};\n  return value as Record<string, JsonValue>;\n}\n\nexport async function ingestAutomationSource(\n  dataDir: string,\n  input: CreateAutomationSourceIngestionRequest,\n): Promise<AutomationSourceIngestionResponse> {\n  if (!input || typeof input !== 'object') {\n    throw new Error('ingestion body is required');\n  }\n  const sourceKind = sourceKindFrom(input.sourceKind);\n  const bodyMarkdown = typeof input.bodyMarkdown === 'string' ? input.bodyMarkdown.trim() : '';\n  if (!bodyMarkdown) throw new Error('bodyMarkdown is required');\n\n  const template = input.templateId ? await getAnyAutomationTemplate(dataDir, input.templateId) : null;\n  const templateSinks = template?.outputSinks ?? ['memory'];\n  const candidateSinks = outputSinksFrom(input.candidateSinks, templateSinks);\n  const reviewPolicy = reviewPolicyFrom(input.reviewPolicy, template?.reviewPolicy ?? 'always');\n  const tokenCompression = compressionModeFrom(\n    input.tokenCompression,\n    template?.tokenCompression ?? 'balanced',\n  );\n  const packetId = `packet_${randomUUID()}`;\n  const sourceEventId = `source_event_${randomUUID()}`;\n  const capturedAt = new Date().toISOString();\n  const sourceRef =\n    optionalString(input.sourceRef) ??\n    optionalString(input.connectorId) ??\n    optionalString(input.artifactId) ??\n    optionalString(input.conversationId) ??\n    sourceKind;","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/automation-ingestions.ts#L356-L392","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait ingestAutomationSource(dataDir, { sourceKind: 'upload', bodyMarkdown: '' });\n// after\nawait ingestAutomationSource(dataDir, { sourceKind: 'upload', bodyMarkdown: '# Notes\\n\\nReal content here.' });","handlingStrategy":"validation","validationCode":"if (typeof input.bodyMarkdown !== 'string' || !input.bodyMarkdown.trim()) {\n  throw new Error('bodyMarkdown is required');\n}","typeGuard":"function hasNonEmptyBodyMarkdown(value: unknown): value is string {\n  return typeof value === 'string' && value.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["automation","ingestion","validation","api","daemon"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}