dagger/dagger · error
encode persisted service host socket %d: nil socket
Error message
encode persisted service host socket %d: nil socket
What it means
While encoding a service for persistence, one of the entries in svc.HostSockets is a nil *Socket. Host sockets record host=>container socket forwards and must carry Kind/Handle/URL data; a nil entry would produce a corrupt payload, so encoding aborts with the index of the offending socket.
Source
Thrown at core/service.go:154
if err != nil {
return dagql.PersistedObjectEncoding{}, err
}
}
if svc.ModuleContext.Self() != nil {
payload.ModuleContextResultID, err = encodePersistedObjectRef(cache, svc.ModuleContext, "service module context")
if err != nil {
return dagql.PersistedObjectEncoding{}, err
}
}
if svc.TunnelUpstream.Self() != nil {
payload.TunnelUpstreamResultID, err = encodePersistedObjectRef(cache, svc.TunnelUpstream, "service tunnel upstream")
if err != nil {
return dagql.PersistedObjectEncoding{}, err
}
}
for i, sock := range svc.HostSockets {
if sock == nil {
return dagql.PersistedObjectEncoding{}, fmt.Errorf("encode persisted service host socket %d: nil socket", i)
}
payload.HostSockets = append(payload.HostSockets, persistedServiceHostSocket{
Kind: sock.Kind,
Handle: sock.Handle,
URLVal: sock.URLVal,
PortForwardVal: sock.PortForwardVal,
SourceClientID: sock.SourceClientID,
})
}
enc, err := json.Marshal(payload)
if err != nil {
return dagql.PersistedObjectEncoding{}, fmt.Errorf("marshal persisted service payload: %w", err)
}
return encodePersistedObjectRawJSON(enc), nil
}
func (*Service) DecodePersistedObject(ctx context.Context, dag *dagql.Server, _ uint64, _ *dagql.ResultCall, payload json.RawMessage) (dagql.Typed, error) {
var persisted persistedServicePayloadView on GitHub (pinned to 82ba2681db)
Solutions
- Find where the nil Socket was appended to HostSockets and skip or error at registration time instead.
- Filter out nil sockets before persisting: svc.HostSockets = slices.DeleteFunc(svc.HostSockets, func(s *Socket) bool { return s == nil }).
- Clear the stale cache entry and re-create the host socket tunnel.
Example fix
// before
svc.HostSockets = append(svc.HostSockets, sock) // sock may be nil
// after
if sock != nil {
svc.HostSockets = append(svc.HostSockets, sock)
} Defensive patterns
Strategy: validation
Validate before calling
for i, sock := range svc.HostSockets {
if sock == nil {
return fmt.Errorf("host socket %d is nil before persisting", i)
}
} Type guard
func hasNilSocket(svc *dagger.Service) bool {
for _, s := range svc.HostSockets { if s == nil { return true } }
return false
} Try / catch
if err != nil && strings.Contains(err.Error(), "nil socket") {
// drop nil sockets and re-persist, or recreate the host tunnel
} Prevention
- Skip nil sockets when appending to HostSockets.
- Re-create host socket tunnels after engine upgrades instead of reusing cached entries.
- Validate socket registration return values before storing them.
When it happens
Trigger: Calling EncodePersistedObject (or persisting any result referencing the service) when svc.HostSockets contains a nil element — e.g. a socket forward registered with a nil *Socket, or HostSockets populated from a deserialized/legacy payload containing null entries.
Common situations: Host socket tunnels (Container.WithExposedPort / host tunnel features) whose socket handles were not properly initialized; results decoded from older cache formats with null hostSocket entries then re-persisted.
Related errors
- encode persisted service: nil service
- encode persisted changeset: nil changeset
- encode persisted client filesync mirror: nil mirror
- encode persisted generated code: nil generated code
- encode persisted container: nil container
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/3258bda113bde3c3.
Report an issue: GitHub.