{"record":{"id":"98a368037fc6a3a4","repo":"fullstackhero/dotnet-starter-kit","slug":"no-current-user-updatechannelcommandhandler","errorCode":null,"errorMessage":"no current user","messagePattern":"no current user","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"src/Modules/Chat/Modules.Chat/Features/v1/Channels/UpdateChannel/UpdateChannelCommandHandler.cs","lineNumber":20,"sourceCode":"using FSH.Framework.Core.Exceptions;\nusing FSH.Modules.Chat.Contracts.v1.Commands;\nusing FSH.Modules.Chat.Data;\nusing FSH.Modules.Chat.Features.v1.Internal;\nusing Mediator;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace FSH.Modules.Chat.Features.v1.Channels.UpdateChannel;\n\npublic sealed class UpdateChannelCommandHandler(\n    ChatDbContext db,\n    ICurrentUser currentUser)\n    : ICommandHandler<UpdateChannelCommand, Unit>\n{\n    public async ValueTask<Unit> Handle(UpdateChannelCommand cmd, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(cmd);\n        var userId = currentUser.GetUserId();\n        if (userId == Guid.Empty) throw new UnauthorizedException(\"no current user\");\n\n        var channel = await db.Channels.FirstOrDefaultAsync(c => c.Id == cmd.ChannelId, cancellationToken)\n            .ConfigureAwait(false)\n            ?? throw new NotFoundException(\"Channel not found.\");\n\n        channel.RequireAdmin(userId.ToString());\n        channel.Rename(cmd.Name, cmd.Description);\n        channel.SetPrivate(cmd.IsPrivate);\n\n        await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n        return Unit.Value;\n    }\n}\n","sourceCodeStart":2,"sourceCodeEnd":34,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Chat/Modules.Chat/Features/v1/Channels/UpdateChannel/UpdateChannelCommandHandler.cs#L2-L34","documentation":"UpdateChannelCommandHandler throws UnauthorizedException(\"no current user\") when the injected ICurrentUser.GetUserId() returns Guid.Empty, meaning no authenticated user is associated with the request. This is a guard at the top of Handle so channel updates never run without an identity. It surfaces as an HTTP 401 via the module's exception middleware.","triggerScenarios":"Calling the update-channel endpoint (PUT/POST for a channel) with a missing, expired, or malformed JWT, or invoking the handler directly (e.g. from a background job or test) without authenticating a user context.","commonSituations":"Anonymous or unauthenticated HTTP requests reaching the endpoint because [Authorize] was omitted or middleware order is wrong; an expired access token the client did not refresh; unit/integration tests constructing the handler with a mocked ICurrentUser returning Guid.Empty.","solutions":["Ensure the caller sends a valid Authorization: Bearer <token> header with an unexpired JWT.","Verify the endpoint is protected with [Authorize] and UseAuthentication/UseAuthorization run before the endpoint mapping.","Fix token acquisition on the client (refresh flow) if the token is expired or invalid.","In tests, mock ICurrentUser.GetUserId() to return a real Guid instead of Guid.Empty."],"exampleFix":"// before\nvar client = factory.CreateClient(); // no auth header\nawait client.PutAsJsonAsync($\"/api/v1/channels/{id}\", req);\n\n// after\nclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(\"Bearer\", token);\nawait client.PutAsJsonAsync($\"/api/v1/channels/{id}\", req);","handlingStrategy":"try-catch","validationCode":"const token = await getValidAccessToken();\nif (!token) throw new Error(\"not authenticated — login before updating a channel\");","typeGuard":"function isAuthenticated(user: { id?: string }): user is { id: string } {\n  return typeof user.id === \"string\" && user.id.length > 0;\n}","tryCatchPattern":"try {\n  await api.updateChannel(cmd);\n} catch (e) {\n  if (e.status === 401) { await refreshToken(); retry(); }\n  else throw e;\n}","preventionTips":["Always attach the bearer token via a central apiFetch interceptor.","Refresh access tokens proactively before expiry.","Keep [Authorize] on every state-changing endpoint.","In tests, never leave ICurrentUser unmocked."],"tags":["auth","unauthorized","chat","current-user"],"backgroundTag":"authentication-required","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}