aaif-goose/goose · error

Failed to parse recipe from file.

Error message

Failed to parse recipe from file.

What it means

Shown by the schedule modal in the goose desktop app when the file was read successfully (found is true, no error) but parseRecipeFromFile returned a falsy value. parseRecipeFromFile delegates to the ACP recipe parser; when the content is empty or is YAML that does not represent a recipe, the parser can return null/undefined rather than throw, and this branch converts that into the localized 'Failed to parse recipe from file.' message. Distinct from error 45: the bytes were read; they just are not a valid goose recipe.

Source

Thrown at ui/desktop/src/components/schedule/ScheduleModal.tsx:143

        }
      }
    }
  }, [isOpen, schedule, initialDeepLink, handleDeepLinkChange]);

  const handleBrowseFile = async () => {
    const fileResponse = await window.electron.selectRecipeFile();
    if (fileResponse) {
      if (fileResponse.filePath.endsWith('.yaml') || fileResponse.filePath.endsWith('.yml')) {
        setRecipeSourcePath(fileResponse.filePath);
        setInternalValidationError(null);

        try {
          if (!fileResponse.found || fileResponse.error) {
            throw new Error(intl.formatMessage(i18n.failedReadFile));
          }
          const recipe = await parseRecipeFromFile(fileResponse.file);
          if (!recipe) {
            throw new Error(intl.formatMessage(i18n.failedParseRecipe));
          }
          setParsedRecipe(recipe);
          if (recipe.title) {
            setScheduleIdFromTitle(recipe.title);
          }
        } catch (e) {
          setParsedRecipe(null);
          setInternalValidationError(
            e instanceof Error ? e.message : intl.formatMessage(i18n.failedParseRecipe)
          );
        }
      } else {
        setInternalValidationError(intl.formatMessage(i18n.invalidFileType));
      }
    }
  };

  const handleLocalSubmit = async (event: FormEvent) => {

View on GitHub (pinned to 3810898a74)

Solutions

  1. Open the selected YAML and confirm it is a goose recipe: top-level title/description plus instructions or prompt
  2. Get known-good YAML by exporting a working recipe (goose CLI or desktop recipe UI) and comparing structure
  3. Make sure the file is non-empty and starts at column 0 (no stray BOM or leading blank document markers)
Defensive patterns

Strategy: validation

Validate before calling

const text = fileResponse.file ?? '';
const looksLikeGooseRecipe =
  text.trim().length > 0 &&
  /(^|\n)(instructions|prompt):/.test(text) &&
  /(^|\n)(title|name):/.test(text);

Type guard

const isParsedRecipe = (r: Recipe | null | undefined): r is Recipe =>
  Boolean(r) && Boolean((r as Recipe).title);

Try / catch

try {
  const recipe = await parseRecipeFromFile(fileResponse.file);
  if (!isParsedRecipe(recipe)) {
    setInternalValidationError(intl.formatMessage(i18n.failedParseRecipe));
    return;
  }
  setParsedRecipe(recipe);
} catch (e) {
  setInternalValidationError(e instanceof Error ? e.message : intl.formatMessage(i18n.failedParseRecipe));
}

Prevention

When it happens

Trigger: Selecting an empty .yaml file; selecting YAML that parses but has no recipe structure (missing title/instructions/prompt at the top level); selecting a YAML front-matter fragment or unrelated config file renamed to .yml.

Common situations: Hand-authoring recipe YAML for the scheduler without the required recipe fields; exporting partial recipes from other tools; picking a CI or docker-compose YAML by mistake because the dialog filters only on extension.

Understand the failure class

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/bbe7717ce2bd5246. Report an issue: GitHub.