{"record":{"id":"0070c197efd7bff4","repo":"gatsbyjs/gatsby","slug":"a-recipe-must-be-specified","errorCode":null,"errorMessage":"A recipe must be specified","messagePattern":"A recipe must be specified","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"deprecated-packages/gatsby-recipes/src/recipe-machine/index.js","lineNumber":45,"sourceCode":"      commands: [],\n      stepResources: [],\n      inputs: {},\n    },\n    states: {\n      resolvingRecipe: {\n        invoke: {\n          id: `resolveRecipe`,\n          src: async (context, _event) => {\n            if (context.src) {\n              return context.src\n            } else if (context.recipePath && context.projectRoot) {\n              const recipe = await resolveRecipe(\n                context.recipePath,\n                context.projectRoot\n              )\n              return recipe\n            } else {\n              throw new Error(`A recipe must be specified`)\n            }\n          },\n          onError: {\n            target: `doneError`,\n            actions: assign({\n              error: (context, _event) => {\n                debug(`error resolving recipe`)\n                return {\n                  error: `Could not resolve recipe \"${context.recipePath}\"`,\n                }\n              },\n            }),\n          },\n          onDone: {\n            target: `parsingRecipe`,\n            actions: assign({\n              recipeSrc: (_context, event) => event.data,\n            }),","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/gatsbyjs/gatsby/blob/8b06340921ffdf23125a365b9c9923690cb62ce6/deprecated-packages/gatsby-recipes/src/recipe-machine/index.js#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass either a full `src` string (MDX content) or both `recipePath` and `projectRoot` to the machine's context.","If using the CLI, supply the recipe argument: `gatsby recipes <name>`.","Validate that `recipePath` is a non-empty string before starting the machine."],"exampleFix":"// before\nMachine.interpret(recipeMachine).start({ src: undefined, recipePath: null, projectRoot: null })\n\n// after\nMachine.interpret(recipeMachine).start({\n  recipePath: `adding-typescript`,\n  projectRoot: process.cwd(),\n})","handlingStrategy":"validation","validationCode":"function machineHasRecipeInput(context) {\n  return Boolean(context.src) || Boolean(context.recipePath && context.projectRoot)\n}","typeGuard":"function isRecipeContextValid(ctx) {\n  return typeof ctx.src === 'string' && ctx.src.trim() !== ''\n    || (typeof ctx.recipePath === 'string' && ctx.recipePath !== '' && typeof ctx.projectRoot === 'string')\n}","tryCatchPattern":"try {\n  await service.send({ type: 'START' })\n} catch (e) {\n  if (/A recipe must be specified/.test(e.message)) { /* ask user for recipe */ }\n}","preventionTips":["Always populate recipePath and projectRoot on machine context.","Validate context shape before starting the machine.","When using the CLI, always pass a recipe argument."],"tags":["recipes","xstate","recipe-machine","deprecated"],"backgroundTag":null,"analyzedSha":"8b06340921ffdf23125a365b9c9923690cb62ce6","analyzedAt":"2026-08-13T02:36:21.405Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}