Django Channels WebSocket connects but immediately closes (Live chat app)
I am building a live streaming website where users can watch a live class and interact via WebSocket chat.
I have integrated Django Channels, Redis, and HLS.js for video playback.
The chat connects via a WebSocket to Django Channels.
However, the issue is:
- The WebSocket connects successfully, but immediately closes afterward.
- The browser console shows multiple attempts to reconnect, but the problem persists.
There is no clear error message except "WebSocket connection closed" in the JavaScript console.
My Setup
- Django 5.1.8
- channels
- channels-redis
- Redis server is running (
redis-cli ping
returnsPONG
) - Development server (local machine,
runserver
+daphne
for Channels)
Code Snippets
HTML/JS Side (live_stream.html)
<script>
// WebSocket Chat functionality
const streamId = "UDOOSDIHOH49849"; // Your stream_id
let chatSocket = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
// You can fetch real username from Django template if available
const username = "Anonymous"; // 🔥 You can dynamically change this later
function connectWebSocket() {
chatSocket = new WebSocket(
'ws://' + window.location.host + '/ws/chat/' + streamId + '/'
);
chatSocket.onopen = function(e) {
console.log('WebSocket connection established');
reconnectAttempts = 0;
};
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
const chatBox = document.getElementById('chatMessages');
const newMessage = document.createElement('p');
newMessage.innerHTML = `<strong>${data.username}:</strong> ${data.message}`;
chatBox.appendChild(newMessage);
chatBox.scrollTop = chatBox.scrollHeight;
};
chatSocket.onclose = function(e) {
console.log('WebSocket connection closed');
if (reconnectAttempts < maxReconnectAttempts) {
console.log('Attempting to reconnect...');
reconnectAttempts++;
setTimeout(connectWebSocket, 3000);
} else {
console.error('Maximum reconnection attempts reached');
}
};
chatSocket.onerror = function(e) {
console.error('WebSocket error:', e);
};
}
// Initial connection
connectWebSocket();
// Send button functionality
document.getElementById('sendBtn').onclick = function() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (message !== '' && chatSocket && chatSocket.readyState === WebSocket.OPEN) {
chatSocket.send(JSON.stringify({
'message': message,
'username': username // Send both message and username
}));
input.value = '';
}
};
// Enter key support
document.getElementById('chatInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('sendBtn').click();
}
});
</script>
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required
def watch_stream(request, stream_id="UDOOSDIHOH49849"):
return render(request, 'video/live_stream.html', {
'stream_id': stream_id,
'user': request.user,
})
consumers.py
# consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
from videoconference_app.models import LiveStream # Import here to avoid app loading issues
try:
self.stream_id = self.scope['url_route']['kwargs']['stream_id']
self.user = self.scope['user']
self.room_group_name = f'chat_{self.stream_id}'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
# Notify others about new user joining
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': f"{self.user.username if self.user.is_authenticated else 'Anonymous'} joined the chat!",
'username': self.user.username if self.user.is_authenticated else 'Anonymous'
}
)
except Exception as e:
print(f"WebSocket connect error: {e}")
await self.close()
async def disconnect(self, close_code):
try:
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
except Exception as e:
print(f"WebSocket disconnect error: {e}")
async def receive(self, text_data):
try:
text_data_json = json.loads(text_data)
message = text_data_json.get('message', '')
username = text_data_json.get('username', 'Anonymous')
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username
}
)
except Exception as e:
print(f"WebSocket receive error: {e}")
async def chat_message(self, event):
try:
await self.send(text_data=json.dumps({
'message': event['message'],
'username': event.get('username', 'Anonymous')
}))
except Exception as e:
print(f"WebSocket chat_message error: {e}")
@database_sync_to_async
def get_stream(self):
from videoconference_app.models import LiveStream
try:
return LiveStream.objects.get(stream_key=self.stream_id)
except LiveStream.DoesNotExist:
return None
routing.py
from django.urls import re_path
from videoconference_app import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<stream_id>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
asgi.py
import os
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from channels.auth import AuthMiddlewareStack
import videoconference_app.routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'course.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
videoconference_app.routing.websocket_urlpatterns
)
),
})
settings.py
(Channels + Redis)
ASGI_APPLICATION = 'course.asgi.application'
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [('127.0.0.1', 6379)],
},
},
}
What I Have Already Tried
- Verified that Redis server is active (
PONG
confirmed). - Checked that WebSocket URL is correct (
ws://
since usinghttp://localhost
). - Confirmed that Daphne and Django run together (
daphne course.asgi:application
). - No visible errors in Django terminal except occasional WebSocket disconnect logs.
- JavaScript client correctly retries but server immediately closes the connection.
Additional Information
- The stream page is protected via
@login_required
, so only authenticated users can access it. - The user object is available in the Django template.
Request
What could be causing the WebSocket to immediately close after connection?
Am I missing anything in the consumer logic, ASGI setup, or Channels configuration?
Any advice or debugging tips would be appreciated!
Thank you in advance.