gatsbyjs/gatsby · error

A recipe must be specified

Error message

A recipe must be specified

What it means

Thrown by the `resolvingRecipe` state of the gatsby-recipes xstate machine (recipe-machine/index.js:45). The state tries three resolution paths in order: explicit `context.src`, a `recipePath`+`projectRoot` pair, else throws. The throw is caught by the machine's `onError` and routed to `doneError` with a friendlier message.

Source

Thrown at deprecated-packages/gatsby-recipes/src/recipe-machine/index.js:45

      commands: [],
      stepResources: [],
      inputs: {},
    },
    states: {
      resolvingRecipe: {
        invoke: {
          id: `resolveRecipe`,
          src: async (context, _event) => {
            if (context.src) {
              return context.src
            } else if (context.recipePath && context.projectRoot) {
              const recipe = await resolveRecipe(
                context.recipePath,
                context.projectRoot
              )
              return recipe
            } else {
              throw new Error(`A recipe must be specified`)
            }
          },
          onError: {
            target: `doneError`,
            actions: assign({
              error: (context, _event) => {
                debug(`error resolving recipe`)
                return {
                  error: `Could not resolve recipe "${context.recipePath}"`,
                }
              },
            }),
          },
          onDone: {
            target: `parsingRecipe`,
            actions: assign({
              recipeSrc: (_context, event) => event.data,
            }),

View on GitHub (pinned to 8b06340921)

Solutions

  1. Pass either a full `src` string (MDX content) or both `recipePath` and `projectRoot` to the machine's context.
  2. If using the CLI, supply the recipe argument: `gatsby recipes <name>`.
  3. Validate that `recipePath` is a non-empty string before starting the machine.

Example fix

// before
Machine.interpret(recipeMachine).start({ src: undefined, recipePath: null, projectRoot: null })

// after
Machine.interpret(recipeMachine).start({
  recipePath: `adding-typescript`,
  projectRoot: process.cwd(),
})
Defensive patterns

Strategy: validation

Validate before calling

function machineHasRecipeInput(context) {
  return Boolean(context.src) || Boolean(context.recipePath && context.projectRoot)
}

Type guard

function isRecipeContextValid(ctx) {
  return typeof ctx.src === 'string' && ctx.src.trim() !== ''
    || (typeof ctx.recipePath === 'string' && ctx.recipePath !== '' && typeof ctx.projectRoot === 'string')
}

Try / catch

try {
  await service.send({ type: 'START' })
} catch (e) {
  if (/A recipe must be specified/.test(e.message)) { /* ask user for recipe */ }
}

Prevention

When it happens

Trigger: Starting the recipe machine without setting `src`, or without setting both `recipePath` and `projectRoot`; passing a falsy/empty recipe path; misconfiguring the machine's initial context.

Common situations: Programmatically invoking the recipe machine from tooling that forgot to populate context; calling `recipes.run()` with no argument; passing a recipe path that is an empty string.

Related errors


AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13). Data as JSON: /api/errors/0070c197efd7bff4. Report an issue: GitHub.