mastra-ai/mastra · error

Factory run requires a board work item

Error message

Factory run requires a board work item

What it means

useStartFactoryRun's mutationFn validates its StartFactoryRunInput before creating a session and starting a run. It requires both a factoryId and a board workItem; if either is missing it throws 'Factory run requires a board work item'. A Factory run always originates from a board work item, so starting without one would produce an invalid kickoff payload to startFactoryRun.

Source

Thrown at mastracode/factory-ui/src/hooks/useStartFactoryRun.ts:94

 * binding, board persistence, and kickoff delivery to the server coordinator.
 * The coordinator commits exact authority before it dispatches any message.
 *
 * The run is started in the background: the board stays put and a toast offers
 * the way into the thread once it exists.
 */
export function useStartFactoryRun() {
  const { factoryId } = useParams<{ factoryId: string }>();
  const factoryQuery = useFactoryQuery(factoryId);
  const { baseUrl } = useApiConfig();
  const navigate = useNavigate();
  const queryClient = useQueryClient();
  const repository = factoryQuery.data?.repositories[0];
  const [phases, setPhases] = useState<Record<string, FactoryRunPhase>>({});

  const mutation = useMutation({
    mutationKey: factoryRunMutationKey(repository?.projectRepositoryId ?? '', factoryId),
    mutationFn: async ({ branch, threadTitle, threadTags, invocation, workItem }: StartFactoryRunInput) => {
      if (!factoryId || !workItem) throw new Error('Factory run requires a board work item');
      if (!repository) throw new Error('Select a repository before starting a Factory run');
      const phaseKey = runPhaseKey({ id: workItem.id, sourceKey: workItem.sourceKey, role: workItem.role });
      const setPhase = (phase: FactoryRunPhase) => setPhases(current => ({ ...current, [phaseKey]: phase }));

      setPhase('workspace');
      const userSession = await createUserSession(baseUrl, repository.projectRepositoryId, { branch });
      const sessionId = userSession.sessionId;
      const desiredStage = workItem.stages.length === 1 ? workItem.stages[0] : undefined;
      if (!isFactoryRuleStage(desiredStage)) throw new Error('Factory runs require one exclusive destination stage');

      setPhase('kickoff');
      const prepared = await startFactoryRun(baseUrl, factoryId, {
        sessionId,
        threadTitle,
        threadTags,
        kickoffKey: crypto.randomUUID(),
        invocation:
          invocation?.type === 'skill'

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Only call start() with a loaded workItem: disable the run action until workItem exists.
  2. Verify factoryId is set before rendering the run controls (it participates in the same guard).
  3. If the board refresh can null out workItem, capture/snapshot the workItem before initiating the run or re-fetch it in the mutation flow.

Example fix

// before
start({ branch, invocation }); // workItem missing

// after
if (workItem) start({ branch, invocation, workItem });
Defensive patterns

Strategy: validation

Validate before calling

const canStart = Boolean(factoryId && workItem);
if (canStart) start({ branch, invocation, workItem });

Type guard

const isRunnableInput = (i: StartFactoryRunInput & { workItem?: WorkItem }): i is StartFactoryRunInput & { workItem: WorkItem } => Boolean(i.workItem);

Try / catch

try { await start(input); } catch (e) { if ((e as Error).message.includes('board work item')) openWorkItemPicker(); }

Prevention

When it happens

Trigger: Calling start({ branch, threadTitle, threadTags, invocation }) without a workItem (or with factoryId falsy) — e.g. a 'Run' button outside the work-item context, or workItem state cleared by a board refresh while the click was in flight.

Common situations: Bulk/run-all UI invoking start for items whose workItem object failed to load; a refactor changed the work item prop name; starting a run from a page where factoryId is only set after selection and the user clicked early.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/34d0e9e355a6db5f. Report an issue: GitHub.