Skip to content

Cannot Unblock after Blocking a user #39189

Description

@rayven2129

Gitea Version

1.27.3

What happened?

Bug: Administrator users cannot be unblocked, and users cannot block an administrator

Description

Gitea 1.27.3 has inconsistent behavior when blocking/unblocking users who have administrator privileges.

A normal user can block another normal user successfully. However, when the target user is assigned administrator privileges, the blocking/unblocking behavior becomes incorrect.

Observed behavior

  • An administrator can block a normal user successfully.
  • A normal user can block another normal user successfully.
  • A normal user cannot block a user who has administrator privileges.
  • If a normal user had already blocked a user and that user is subsequently assigned administrator privileges, the existing block relationship remains in the database, but the normal user can no longer unblock that administrator through the web UI.
  • The unblock request returns HTTP 400 Bad Request.

Gitea Version

Gitea version 1.27.3
built with go1.26.7-X:jsonv2

Docker image:

gitea/gitea:latest

Environment

  • Gitea 1.27.3
  • SQLite3
  • Nginx reverse proxy
  • Gitea HTTP port 3000

Scenario 1: Administrator blocks a normal user

When logged in as an administrator:

Administrator → blocks → Normal user

The operation succeeds.

Scenario 2: Normal user blocks another normal user

When both users are normal users:

Normal user A → blocks → Normal user B

The operation succeeds.

Scenario 3: Normal user attempts to block an administrator

When the target user has administrator privileges:

Normal user → blocks → Administrator

The operation fails.

Scenario 4: Existing block becomes an administrator

This is the reproducible case.

Initially:

rayven.delacruz → blocks → root

At the time the block was created, the relationship exists in the database:

blocker_id = 2
blockee_id  = 1

The root user is subsequently assigned administrator privileges.

The existing block relationship remains:

id  blocker_id  blockee_id
1   2           1

However, attempting to unblock root from:

Settings → Blocked Users

submits:

action=unblock
blockee=root

and Gitea returns:

400 Bad Request

The block relationship remains in the database.

Relevant Source Code

In Gitea 1.27.3, BlockedUsersPost() calls:

case "unblock":
    err = user_service.UnblockUser(ctx, ctx.Doer, blocker, blockee)

UnblockUser() calls:

if !CanUnblockUser(ctx, doer, blocker, blockee) {
    return user_model.ErrCanNotUnblock
}

CanUnblockUser() calls:

if !user_model.IsUserBlockedBy(ctx, blockee, blocker.ID) {
    return false
}

However, IsUserBlockedBy() contains:

if blockee.IsAdmin {
    return false
}

before checking the user_blocking table.

Therefore, if the blockee is an administrator, the function returns false even when an actual block relationship exists.

The subsequent flow becomes:

Administrator blockee
        ↓
IsUserBlockedBy() returns false
        ↓
CanUnblockUser() returns false
        ↓
ErrCanNotUnblock
        ↓
HTTP 400 Bad Request

Database Evidence

The user_blocking table contains:

id  blocker_id  blockee_id
1   2           1

The users are:

1 | root
2 | rayven.delacruz

Therefore:

rayven.delacruz (ID 2)
        ↓
     blocks
        ↓
root (ID 1)

The relationship exists even though root is an administrator.

Expected Behavior

Administrator status should not make an existing block relationship invisible to the unblock operation.

If:

User A → blocks → User B

and User B is subsequently granted administrator privileges, User A should still be able to manage/remove their existing block relationship.

Additionally, the behavior of blocking should be consistent and clearly defined for administrator accounts.

Actual Behavior

When the target user is an administrator:

Normal user → Administrator

the normal user cannot block the administrator.

If a previously blocked user is subsequently made an administrator, the existing block remains in the database but cannot be removed through the web UI.

Relevant Logs

2026/08/31 09:31:36 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:45094, 400 Bad Request in 5.1ms @ setting/block.go:31(setting.BlockedUsersPost)

2026/08/31 09:31:37 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:45100, 400 Bad Request in 6.7ms @ setting/block.go:31(setting.BlockedUsersPost)

Suspected Cause

The following check in IsUserBlockedBy() appears to cause the inconsistency:

if blockee.IsAdmin {
    return false
}

This causes administrator users to be treated as if they are not blocked, even when a corresponding user_blocking database record exists.

Suggested Fix

Consider separating the concepts of:

  1. Whether a user is currently blocked.
  2. Whether a user is allowed to initiate a new block.
  3. Whether an existing block can be removed.

IsUserBlockedBy() should potentially continue to report an existing block relationship regardless of the blockee's administrator status, while permission checks for creating or managing blocks can be handled separately.

Impact

This can leave stale/unmanageable block relationships when a user changes administrator status.

It also creates inconsistent behavior depending on whether the target user is an administrator.

Reproducibility

The issue is reproducible consistently on Gitea 1.27.3.

Image

From server logs

itea.1.zb3627ziv7er@ldap | 2026/08/30 13:51:50 cmd/web.go:231:serveInstalled() [I] PID: 17 Gitea Web Finished
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:32 HTTPRequest [I] router: completed GET /user/settings/blocked_users for 10.5.0.4:43478, 200 OK in 17.2ms @ setting/block.go:19(setting.BlockedUsers)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:33 HTTPRequest [I] router: completed GET /avatar/627624ba36c0f5cbaf9b3bbe7c992d11?size=48 for 10.5.0.4:43490, 303 See Other in 16.8ms @ user/avatar.go:38(user.AvatarByEmailHash)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:36 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:45094, 400 Bad Request in 5.1ms @ setting/block.go:31(setting.BlockedUsersPost)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:37 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:45100, 400 Bad Request in 6.7ms @ setting/block.go:31(setting.BlockedUsersPost)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:37 HTTPRequest [I] router: polling GET /user/events for 10.5.0.4:43532, elapsed 3929.5ms @ events/events.go:18(events.Events)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:31:38 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:45108, 400 Bad Request in 6.7ms @ setting/block.go:31(setting.BlockedUsersPost)
gitea.1.isvocrqxants@ldap | 2026/08/31 09:40:02 HTTPRequest [I] router: completed POST /user/settings/blocked_users for 10.5.0.4:42300, 400 Bad Request in 11.5ms @ setting/block.go:31(setting.BlockedUsersPost)

How are you running Gitea?

No response

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions