nexu-io/open-design · error

proposal title is required

Error message

proposal title is required

What it means

Thrown by createAutomationProposal() when input.title is not a string or is empty/whitespace-only. title is required because it becomes the proposal's display name and, for skill/design-system targets, the fallback slug.

Source

Thrown at apps/daemon/src/automation-proposals.ts:79

export async function getAutomationProposal(
  dataDir: string,
  id: string,
): Promise<AutomationEvolutionProposal | null> {
  const proposals = await listAutomationProposals(dataDir, { status: 'all' });
  return proposals.find((proposal) => proposal.id === id) ?? null;
}

export async function createAutomationProposal(
  dataDir: string,
  input: CreateAutomationEvolutionProposalRequest & {
    id?: string;
    status?: AutomationProposalStatus;
  },
): Promise<AutomationEvolutionProposal> {
  const now = new Date().toISOString();
  if (!input || typeof input !== 'object') throw new Error('proposal body is required');
  if (typeof input.title !== 'string' || !input.title.trim()) {
    throw new Error('proposal title is required');
  }
  if (typeof input.summary !== 'string' || !input.summary.trim()) {
    throw new Error('proposal summary is required');
  }
  if (!input.patch || typeof input.patch !== 'object') {
    throw new Error('proposal patch is required');
  }
  const status =
    input.status && VALID_STATUSES.has(input.status)
      ? input.status
      : 'pending-review';
  const proposal: AutomationEvolutionProposal = {
    id:
      typeof input.id === 'string' && input.id.trim()
        ? input.id.trim()
        : `proposal_${randomUUID()}`,
    title: input.title.trim(),
    summary: input.summary.trim(),

View on GitHub (pinned to 5be4028344)

Solutions

  1. Provide a non-empty title string.
  2. Map any client-side 'name'/'label' field to title before calling.
  3. For agent-generated proposals, derive a title from the first line of the patch body if one is not explicit.

Example fix

// before
await createAutomationProposal(dataDir, { title: '   ', summary: 'S', patch: { format: 'json', after: '{}' } });
// after
await createAutomationProposal(dataDir, { title: 'Add pricing memory', summary: 'S', patch: { format: 'json', after: '{}' } });
Defensive patterns

Strategy: validation

Validate before calling

if (typeof input.title !== 'string' || !input.title.trim()) {
  throw new Error('proposal title is required');
}

Type guard

function hasNonEmptyTitle(value: unknown): value is string {
  return typeof value === 'string' && value.trim().length > 0;
}

Prevention

When it happens

Trigger: POST to the proposals endpoint (or a direct call) with title omitted, empty, whitespace-only, or a non-string. The guard fires before summary and patch checks.

Common situations: Client form missing the title field; agent-generated proposal that left the title blank; field named differently in the client (e.g. 'name') and never mapped to title.

Related errors


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