aaif-goose/goose · error

Invalid deeplink or recipe format

Error message

Invalid deeplink or recipe format

What it means

Thrown by the recipe import dialog in the goose desktop app when parseDeeplink returns null. parseDeeplink (ui/desktop/src/recipe/index.ts:93) returns null — never throws — whenever the trimmed input does not start with 'goose://recipe?config=', has an empty payload, fails decodeRecipe, or the decoded recipe lacks title/description or both instructions and prompt. The form collapses all of those distinct failures into this single message.

Source

Thrown at ui/desktop/src/components/recipes/ImportRecipeForm.tsx:126

  const importRecipeForm = useForm({
    defaultValues: {
      deeplink: '',
      recipeUploadFile: null as File | null,
    },
    validators: {
      onChange: importRecipeSchema,
    },
    onSubmit: async ({ value }) => {
      setImporting(true);
      try {
        let recipe: Recipe;

        // Parse recipe from either deeplink or recipe file
        if (value.deeplink && value.deeplink.trim()) {
          const parsedRecipe = await parseDeeplink(value.deeplink.trim());
          if (!parsedRecipe) {
            throw new Error('Invalid deeplink or recipe format');
          }
          recipe = parsedRecipe;
        } else {
          const fileContent = await value.recipeUploadFile!.text();
          recipe = await parseRecipeFromFile(fileContent);
        }

        await saveRecipe(recipe, null);

        // Reset dialog state
        importRecipeForm.reset({
          deeplink: '',
          recipeUploadFile: null,
        });
        onClose();

        onSuccess();

View on GitHub (pinned to 3810898a74)

Solutions

  1. Paste a link that starts exactly with goose://recipe?config= — generate one from the deeplink generator page or the desktop share flow
  2. If importing a YAML file, leave the deeplink field empty and use the file picker; a non-empty deeplink always takes precedence
  3. Re-copy the entire link (truncated config payloads fail decode)
  4. If you generate the links yourself, ensure the encoded recipe has title, description, and instructions or prompt

Example fix

// before
const parsedRecipe = await parseDeeplink(value.deeplink.trim());
if (!parsedRecipe) {
  throw new Error('Invalid deeplink or recipe format');
}

// after
const link = value.deeplink.trim();
if (!link.startsWith('goose://recipe?config=')) {
  importRecipeForm.setError('deeplink', 'Link must start with goose://recipe?config=');
  return;
}
const parsedRecipe = await parseDeeplink(link);
if (!parsedRecipe) {
  importRecipeForm.setError(
    'deeplink',
    'Decoded recipe is invalid: it needs title, description, and instructions or prompt'
  );
  return;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const link = value.deeplink.trim();
if (link && !link.startsWith('goose://recipe?config=')) {
  importRecipeForm.setError('deeplink', 'Link must start with goose://recipe?config=');
}

Type guard

const isRecipe = (r: Recipe | null): r is Recipe =>
  r !== null &&
  Boolean(r.title) &&
  Boolean(r.description) &&
  (Boolean(r.instructions) || Boolean(r.prompt));

Try / catch

try {
  const recipe = await parseDeeplink(link);
  if (!isRecipe(recipe)) {
    importRecipeForm.setError('deeplink', 'Recipe needs title, description, and instructions or prompt');
    return;
  }
  await saveRecipe(recipe, null);
} catch (e) {
  importRecipeForm.setError('deeplink', e instanceof Error ? e.message : 'Import failed');
} finally {
  setImporting(false);
}

Prevention

When it happens

Trigger: Pasting raw recipe YAML into the deeplink field; pasting a goose:// link for a non-recipe target such as goose://extension?...; a config= payload that is truncated so base64/decode fails; a decoded recipe that has title and description but neither instructions nor prompt.

Common situations: Confusing the deeplink field with the file import path; links copied from chat clients that ellipsize long URLs; recipes authored by hand that omit instructions/prompt; whitespace or a newline inside the pasted link breaking the prefix check or payload.

Related errors


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