{"record":{"id":"7f2930cce8a82370","repo":"dagger/dagger","slug":"main-client-caller-q-was-nil","errorCode":null,"errorMessage":"main client caller %q was nil","messagePattern":"main client caller %q was nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/server/session.go","lineNumber":2725,"sourceCode":"\t}\n\treturn client.daggerSession.withClosingCancel(context.WithoutCancel(ctx)), nil\n}\n\nfunc (srv *Server) sessionMainClientConn(ctx context.Context, sess *daggerSession) (*grpc.ClientConn, error) {\n\t_ = ctx\n\tif sess == nil {\n\t\treturn nil, errors.New(\"session is nil\")\n\t}\n\tclient, err := srv.clientFromIDs(sess.sessionID, sess.mainClientCallerID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get main client %q: %w\", sess.mainClientCallerID, err)\n\t}\n\tcaller, err := client.getClientCaller(ctx, sess.mainClientCallerID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get main client caller %q: %w\", sess.mainClientCallerID, err)\n\t}\n\tif caller == nil {\n\t\treturn nil, fmt.Errorf(\"main client caller %q was nil\", sess.mainClientCallerID)\n\t}\n\tconn := caller.Conn()\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"main client conn %q was nil\", sess.mainClientCallerID)\n\t}\n\treturn conn, nil\n}\n\n// The nearest ancestor client that is not a module (either a caller from the host like the CLI\n// or a nested exec). Useful for figuring out where local sources should be resolved from through\n// chains of dependency modules.\nfunc (srv *Server) nonModuleParentClient(ctx context.Context) (*daggerClient, error) {\n\tclient, err := srv.clientFromContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif client.mod.Self() == nil {\n\t\t// not a module client, return the current client","sourceCodeStart":2707,"sourceCodeEnd":2743,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/engine/server/session.go#L2707-L2743","documentation":"getClientCaller succeeded but returned a nil caller for the session's main client — an invariant violation, since the main client that created the session should always have a concrete caller. Defensive check against internal state bugs or teardown races.","triggerScenarios":"sessionMainClientConn running while the main client's caller entry is being removed, or a registration path that stored a nil caller.","commonSituations":"Session shutdown racing with core API calls needing the main conn; engine bugs in caller registration order.","solutions":["Treat as an internal bug: capture engine logs/trace and report","Avoid invoking paths needing the main conn during shutdown","Check newer Dagger versions for race fixes","Add defensive nil checks in forked code before use"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"conn, err := srv.sessionMainClientConn(ctx, sess)\nif err != nil {\n    if strings.Contains(err.Error(), \"was nil\") {\n        // internal invariant violation: capture diagnostics\n    }\n    return err\n}","preventionTips":["Treat nil callers as engine bugs and report with logs","Serialize session teardown with conn resolution","Add regression tests around main-client removal"],"tags":["dagger","engine","session","internal"],"backgroundTag":"nil-session-state","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}