dagger/dagger · error

object %q function %q is marked @agent but does not return L

Error message

object %q function %q is marked @agent but does not return LLM!; @agent functions must have the agent(base: LLM!): LLM! shape

What it means

An @agent function must have the exact shape agent(base: LLM!): LLM! — it is invoked by a compose fold that supplies an LLM base and expects a new LLM back. validateAgentFunction rejects any @agent function whose return type is not the core LLM object (core/module.go:243), failing module load.

Source

Thrown at core/module.go:243

	}
	for _, argRes := range fn.Args {
		arg := argRes.Self()
		if argRequired(arg) {
			return fmt.Errorf("object %q function %q is marked @generate but declares required argument %q; @generate functions must be callable with no arguments",
				obj.OriginalName, fn.OriginalName, arg.OriginalName)
		}
	}
	return nil
}

// validateAgentFunction enforces the @agent middleware contract (hack/designs/workspace-agents.md
// §3): the function must return LLM! and must declare exactly one required
// argument, an LLM! (the base the compose fold supplies, whatever it is
// named). A non-LLM! return, a missing base, or any other required argument is
// a hard error at module load.
func validateAgentFunction(obj *ObjectTypeDef, fn *Function) error {
	if !returnsCoreObject(fn, "LLM") {
		return fmt.Errorf("object %q function %q is marked @agent but does not return LLM!; @agent functions must have the agent(base: LLM!): LLM! shape",
			obj.OriginalName, fn.OriginalName)
	}
	baseExempted := false
	for _, argRes := range fn.Args {
		arg := argRes.Self()
		if !argRequired(arg) {
			continue
		}
		if !baseExempted && isCoreLLMArg(arg) {
			baseExempted = true
			continue
		}
		return fmt.Errorf("object %q function %q is marked @agent but declares required argument %q; an @agent function may only require a single LLM! argument (the base the compose fold supplies)",
			obj.OriginalName, fn.OriginalName, arg.OriginalName)
	}
	if !baseExempted {
		return fmt.Errorf("object %q function %q is marked @agent but does not declare a required LLM! argument; @agent functions must have the agent(base: LLM!): LLM! shape (the base the compose fold supplies)",
			obj.OriginalName, fn.OriginalName)

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Change the return type to the core LLM! type and return the composed LLM
  2. Return the base LLM with the prompt/step applied (e.g. base.withPrompt(...))
  3. Remove the @agent pragma if the function is not part of an agent compose fold

Example fix

// before
@agent()
answer(base: LLM): string { return base.query("..."); }
// after
@agent()
answer(base: LLM): LLM { return base.withPrompt("..."); }
Defensive patterns

Strategy: validation

Validate before calling

if (fn.pragmas.includes("agent") && !(fn.returnType === "LLM" && fn.returnNonNull)) {
  throw new Error(`@agent ${fn.name} must return LLM!, got ${fn.returnType}`);
}

Type guard

const returnsLLM = (fn) => fn.returnType === "LLM" && fn.returnNonNull === true;

Prevention

When it happens

Trigger: Decorating a function with @agent that returns String, Void, a custom object, or nullable LLM instead of core LLM!.

Common situations: Authoring an agent step that returns a final answer string instead of the composed LLM; adapting an ordinary helper into an @agent function without adjusting its signature.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/0cb261f932cb4232. Report an issue: GitHub.