OtterMind/Chat2DB · error · BusinessException

largeCellValue.tokenForbidden

largeCellValue.tokenForbidden

Error message

largeCellValue.tokenForbidden

What it means

Thrown by DbLargeValueTokenServiceImpl.validateOwner when the token's userId or organizationId does not match the current Context (context.getLoginUser().getId() / context.getOrganizationId()). Tokens are scoped to the user+org that triggered the result render, so a mismatch is treated as unauthorized access. Resolves to 'You do not have permission to access this large value'. This is an authorization boundary.

Source

Thrown at chat2db-community-server/chat2db-community-domain/chat2db-community-domain-core/src/main/java/ai/chat2db/community/domain/core/impl/db/DbLargeValueTokenServiceImpl.java:154

                .primaryKey(primaryKey)
                .userId(userId)
                .organizationId(organizationId)
                .expiresAt(Instant.now().plus(DEFAULT_TTL))
                .valueType(cell.getValueType())
                .sqlType(cell.getSqlType())
                .columnType(cell.getColumnType())
                .sizeBytes(cell.getSizeBytes())
                .sizeChars(cell.getSizeChars())
                .build();
    }

    private void validateOwner(LargeValueToken token) {
        Context context = ContextUtils.queryContext();
        Long organizationId = context == null ? null : context.getOrganizationId();
        LoginUser loginUser = context == null ? null : context.getLoginUser();
        Long userId = loginUser == null ? null : loginUser.getId();
        if (!Objects.equals(token.getUserId(), userId) || !Objects.equals(token.getOrganizationId(), organizationId)) {
            throw new BusinessException("largeCellValue.tokenForbidden");
        }
    }

    private Object getLocatorValue(ResultCell cell) {
        return cell.getRawValue() == null ? cell.getValue() : cell.getRawValue();
    }
}

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Only request large values for cells rendered in the current user's own session.
  2. Ensure ContextUtils.queryContext() carries a valid LoginUser before calling requireValid.
  3. Do not share largeValueId values between users; they are per-user opaque references.
Defensive patterns

Strategy: validation

Validate before calling

Context ctx = ContextUtils.queryContext();
LoginUser u = ctx == null ? null : ctx.getLoginUser();
if (u == null || u.getId() == null) {
    // require login before large-value access
}

Type guard

boolean tokenOwnedByCaller(LargeValueToken t) {
    Context c = ContextUtils.queryContext();
    LoginUser u = c == null ? null : c.getLoginUser();
    Long uid = u == null ? null : u.getId();
    Long oid = c == null ? null : c.getOrganizationId();
    return Objects.equals(t.getUserId(), uid) && Objects.equals(t.getOrganizationId(), oid);
}

Prevention

When it happens

Trigger: One user attempting to read a large-cell token issued to another user; an organization mismatch (multi-tenant); a token id leaked/replayed in a different session; context not set so both ids resolve to null but the token was issued under a real user.

Common situations: Shared/copy-pasted URLs containing a largeValueId across users; a session that lost its login context (both ids null) trying to read a user-bound token; cross-tenant token reuse.

Understand the failure class

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/06ac96208cf4c638. Report an issue: GitHub.