Skip to content

byo harness injects no PORT but advertises the agent on :80, so a BYO image built with go/adk/pkg/app is READY and unreachable #2758

Description

@Rachael-Graham

Disclosure: this issue was written by an AI agent (Claude) while verifying what a
bring-your-own-agent guide must tell readers, and reviewed by a human before filing.

Summary

The byo harness compiler injects no PORT, but the agent card it builds advertises the
agent at http://127.0.0.1:80. kagent's own BYO helper (go/adk/pkg/app) defaults to
8080. So a BYO image built the documented way reports READY and then fails every
invoke, because the gateway dials a port nothing is listening on.

The instance being READY is what makes this bad: readiness is probed on a different
port that succeeds regardless, so nothing surfaces the mismatch.

Environment

kagent b8d53d39 (chart kagent-v0.10.0-rc1-129-gb8d53d39), substrate 0.0.25, kind, Kubernetes 1.35.

Reproduction

Built a BYO image from go/core/test/byoa2a/main.go with the one line Port: "80" removed,
so app.New falls back to PORT and then to its 8080 default:

application, err := app.New(app.AppConfig{
    AgentCard: a2atype.AgentCard{Name: "byoport", Version: "v1", ...},
    AppName: "byoport", Logger: logger,       // no Port set
}, executor{})

Harness and AgentTemplate:

apiVersion: kagent.dev/v1alpha3
kind: Harness
metadata: {name: byoport, namespace: kagent}
spec:
  byo: {}
  allowedAgentTemplates: {selector: {matchLabels: {kagent.dev/harness: byoport}}}
  workload:
    image: localhost:5001/byoport@sha256:ea596db3dac8da570980143210efeb2b47bcfb0a3afc5aa0f325a6063c5cf009
    command: ["/byoport"]
  substrate:
    workerPoolRef: {name: kagent-default}
    snapshotPolicy: {location: s3://ate-snapshots/kagent}

Result — the template goes fully ready:

Accepted=True(Accepted) ResolvedRefs=True(Resolved) Compatible=True(Compatible) Ready=True(Ready)

The AgentInstance is created READY, and the invoke fails:

$ kagent create agent-instance --agent-template byoport-agent --harness byoport
| 01a08301-8cd0-72c8-818f-26c7490ce37d | byoport-agent | byoport | READY | ... |

$ kagent invoke --agent-instance 01a08301-8cd0-72c8-818f-26c7490ce37d --task "hello"
Error: invoke AgentInstance: upstream call failed: Connect: tunnel failed

Adding PORT to the Harness fixes it with no image change:

kubectl patch harness byoport -n kagent --type=merge \
  -p '{"spec":{"env":[{"name":"PORT","value":"80"}]}}'
$ kagent invoke --agent-instance <new id> --task "hello"
BYO agent response

That is the proof: same image, same command, only PORT differs.

Why it happens

  • go/core/internal/translator/kagent/compiler.go:73 injects corev1.EnvVar{Name: "PORT", Value: "80"}.
  • go/core/internal/translator/byo/compiler.go injects no PORT. Its environment is
    only compiled.Environment plus adkconfig.HarnessEnvironment(harness), i.e. the
    Harness's own spec.env.
  • go/core/internal/translator/byo/compiler.go:64 nonetheless builds the card with
    SupportedInterfaces: [{URL: "http://127.0.0.1:80", ...}].
  • go/adk/pkg/app/app.go:23,161-165cfg.Port -> PORT env -> defaultPort = "8080".

The e2e image passes only because go/core/test/byoa2a/main.go:40 hardcodes Port: "80".
The other in-repo reference, go/adk/examples/byo/main.go, documents PORT as
"default 8080" and would not work unpinned.

Why readiness does not catch it

go/core/internal/substrate/actor_template.go:69-71 probes /readyz on port 8081, and
go/adk/pkg/a2a/server/server.go:134 serves that on :8081 independently of PORT. So the
health port is satisfied while the A2A port is wrong.

Expected

One of:

  1. byo/compiler.go injects PORT=80 the way the kagent compiler does, so the card and the
    runtime agree by construction; or
  2. the card is built from the port the image actually uses, with a documented field for it; or
  3. the mismatch is rejected or surfaced as a condition instead of presenting as READY.

Option 1 matches existing behaviour for the other harnesses and needs no API change.

Adjacent, not tested

byo/compiler.go:64 sets ProtocolBinding: TransportProtocolGRPC while app.New
advertises a2atype.NewAgentInterface("/", TransportProtocolJSONRPC) (app.go:180).
Whether the gateway cares was not tested here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions