Skip to content

Troubleshooting Guide

Solutions to common problems when using the LIFX Emulator.

Port Conflicts and Resolution

Port Already in Use

Problem: Server fails to start with "Address already in use"

OSError: [Errno 48] Address already in use

Causes:

  • Another emulator instance is running
  • Another application is using port 56700
  • Previous test didn't clean up properly

Solutions:

  1. Find what's using the port:

    # Linux/macOS
    lsof -i :56700
    netstat -an | grep 56700
    
    # Windows
    netstat -ano | findstr :56700
    

  2. Kill the process:

    # Linux/macOS
    kill <PID>
    
    # Windows
    taskkill /PID <PID> /F
    

  3. Use dynamic port allocation (best for tests):

    # Let OS assign available port
    server = EmulatedLifxServer([device], "127.0.0.1", 0)
    await server.start()
    print(f"Server running on port {server.port}")
    

  4. Use port offset in parallel tests:

    @pytest.fixture
    async def emulator(worker_id):
    
          port = 56700 + int(worker_num) + 1
    
        device = create_color_light(f"d073d5{worker_num:06d}")
        server = EmulatedLifxServer([device], "127.0.0.1", port)
        async with server:
            yield server
    

  5. Wait between tests:

    import asyncio
    
    await server.stop()
    await asyncio.sleep(0.1)  # Let OS release the port
    

Port Permission Denied

Problem: Cannot bind to port (Linux/macOS)

PermissionError: [Errno 13] Permission denied

Cause: Ports below 1024 require root privileges

Solutions:

  1. Use port >= 1024 (recommended):

    server = EmulatedLifxServer([device], "127.0.0.1", 56700)
    

  2. Don't run as root (security risk)

Discovery Failures and Debugging

Client Cannot Find Emulated Devices

Problem: LIFX client library discovers nothing

Diagnostic checklist:

  1. Check emulator is running:

    lifx-emulator --verbose
    
    Should show: Emulator listening on 127.0.0.1:56700

  2. Check bind address:

    # only localhost
    server = EmulatedLifxServer([device], "127.0.0.1", 56700)
    
    # all interfaces
    server = EmulatedLifxServer([device], "0.0.0.0", 56700)
    

  3. Check firewall:

  4. Windows: Allow Python through Windows Firewall

  5. macOS: System Preferences → Security & Privacy → Firewall → Allow Python
  6. Linux:

    sudo ufw allow 56700/udp
    

  7. Check client port:

  8. Ensure client is looking on the correct port

  9. Default LIFX port is 56700
  10. If using custom port, client must match

  11. Network isolation:

  12. Docker containers need --network host or proper port mapping

  13. VMs need bridged networking
  14. WSL2 may need port forwarding

  15. Enable verbose logging:

import logging
logging.basicConfig(level=logging.DEBUG)

Discovery Works, but Device Not Responding

Problem: Client sees device but times out on commands

Solutions:

  1. Check target serial:
device = create_color_light("d073d5000001")
print(f"Device serial: {device.state.serial}")
# Client must target this exact serial
  1. Check tagged packets:

  2. Broadcast packets have tagged=True in header

  3. Unicast packets have tagged=False and specific target
  4. Emulator routes based on target field

  5. Check ack_required and res_required flags:

# Client should set these flags appropriately
# ack_required=True → Device sends acknowledgment
# res_required=True → Device sends state response
  1. Enable packet logging:

lifx-emulator --verbose
This shows all packets sent/received:
RX: GetService (2) from ('127.0.0.1', 54321)
TX: StateService (3) to ('127.0.0.1', 54321)

Devices Discovered Multiple Times

Problem: Client sees duplicate devices

Cause: Multiple emulator instances or broadcast responses

Solutions:

  1. Check for multiple instances:

    ps aux | grep lifx-emulator
    

  2. Use unique serials:

    # Wrong - duplicate serials
    device1 = create_color_light("d073d5000001")
    device2 = create_color_light("d073d5000001")  # Same serial!
    
    # Right - unique serials
    device1 = create_color_light("d073d5000001")
    device2 = create_color_light("d073d5000002")
    

  3. Check network interfaces:

  4. Multiple interfaces may cause duplicate broadcasts
  5. Bind to specific interface to avoid this

Timeout Issues

Client Operations Timeout

Problem: Client commands timeout waiting for response

Diagnostic steps:

  1. Increase client timeout: Check your client library documentation for details.

  2. Check response scenarios:

    # Are you testing timeouts intentionally?
    device.scenarios = {
        'drop_packets': {102: 1.0},  # Will cause timeouts
    }
    

  3. Check async context:

    # Wrong - server has to be started manually
    server = EmulatedLifxServer([device], "127.0.0.1", 56700)
    # ... try to communicate ...
    
    # Right - server starts automatically
    async with EmulatedLifxServer([device], "127.0.0.1", 56700) as server:
        # ... communicate here ...
    

  4. Network latency:

    # Check if delay scenarios are configured
    device.scenarios = {
        'response_delays': {102: 10.0},  # 10 second delay!
    }
    

Tests Timeout in CI/CD

Problem: Tests pass locally but timeout in CI

Solutions:

  1. Use dynamic ports:

    def get_free_port() -> int:
     """Get a free UDP port."""
     with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
         s.bind(("127.0.0.1", 0))
         return s.getsockname()[1]
    
    port = get_free_port()
    server = EmulatedLifxServer([device], "127.0.0.1", port)
    

  2. Increase pytest timeout:

    # pytest.ini or pyproject.toml
    [tool.pytest.ini_options]
    timeout = 30
    

  3. Reduce test fixture scope:

    # Module scope for faster tests
    @pytest.fixture(scope="module")
    async def emulator():
        ...
    

  4. Check CI resource limits:

  5. CI runners may be slower than local machine

  6. Increase timeouts for CI environment
  7. Use conditional timeouts:
    import os
    TIMEOUT = 10 if os.getenv('CI') else 5
    

Protocol Errors and Interpretation

Invalid Packet Type Errors

Problem: "Unknown packet type" errors

Cause: Client sending unsupported packet type

Solution:

  1. Check protocol version:

  2. Emulator supports LIFX LAN Protocol (November 2025)

  3. See https://github.com/LIFX/public-protocol for specification

  4. Check packet type support:

    from lifx_emulator.protocol.packets import PACKET_REGISTRY
    print(f"Supported packet types: {list(PACKET_REGISTRY.keys())}")
    

  5. Enable verbose logging:

    lifx-emulator --verbose
    
    Look for: Unknown packet type: XXX

Malformed Packet Errors

Problem: "Failed to unpack packet" errors

Causes:

  • Incorrect header format
  • Wrong packet structure
  • Byte order issues

Solutions:

  1. Check header format:

  2. Header is exactly 36 bytes

  3. Target field is 8 bytes (6-byte MAC + 2 null bytes)
  4. Packet type in bytes 32-33 (little-endian)

  5. Verify packet structure:

    from lifx_emulator.protocol.packets import Light
    
    # Check expected structure
    print(f"GetColor packet type: {Light.GetColor.PKT_TYPE}")
    
    # Check payload size
    packet = Light.GetColor()
    data = packet.pack()
    print(f"Payload size: {len(data)} bytes")
    

  6. Enable debug logging:

    import logging
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    )
    

Acknowledgment Not Received

Problem: Client expects ack but doesn't receive it

Causes:

  • ack_required flag not set in header
  • Packet dropped by scenario configuration
  • Network issues

Solutions:

  1. Check ack_required flag:

    # Client must set ack_required=True in header
    # Emulator automatically sends Acknowledgment (type 45)
    

  2. Check scenarios:

    # Is ack being dropped?
    device.scenarios = {
        'drop_packets': {45: 1.0},  # Drops acknowledgments!
    }
    

  3. Enable verbose logging:

    lifx-emulator --verbose
    
    Look for: TX: Acknowledgment (45) to ...

Performance Problems

Slow Test Execution

Problem: Tests take too long to run

Solutions:

  1. Use appropriate fixture scopes:

    # Bad - function scope (starts server for each test)
    @pytest.fixture
    async def emulator():
        ...
    
    # Good - module scope (starts once per file)
    @pytest.fixture(scope="module")
    async def emulator():
        ...
    

  2. Run tests in parallel:

    pip install pytest-xdist
    pytest -n auto
    

  3. Reduce device count:

    # Use only devices you need
    # Wrong - creating 100 devices for a simple test
    devices = [create_color_light(f"d073d5{i:06d}") for i in range(100)]
    
    # Right - one device is enough
    device = create_color_light("d073d5000001")
    

  4. Remove unnecessary delays:

    # Don't do this
    await asyncio.sleep(1)  # Waiting "just in case"
    
    # The emulator responds instantly (no need to wait)
    

  5. Profile your tests:

    pytest --durations=10
    

High Memory Usage

Problem: Emulator consuming too much memory

Causes:

  • Too many devices
  • Large tile/zone counts
  • Memory leak in test code

Solutions:

  1. Limit device count:

    # Each device uses ~1-5 MB
    # 1000 devices = ~5 GB
    
    # Use only what you need
    devices = [create_color_light(f"d073d5{i:06d}") for i in range(10)]
    

  2. Clean up properly:

    # Ensure server stops and releases resources
    async with server:
        # ... tests ...
    # Cleanup happens automatically
    

  3. Check for test isolation issues:

    # Are you accumulating state?
    # Use fresh fixtures for each test
    

Packet Loss Under Load

Problem: Packets dropped at high throughput

Causes: - OS UDP buffer limits - CPU saturation - Network congestion

Solutions:

  1. Increase OS UDP buffer:

    # Linux
    sudo sysctl -w net.core.rmem_max=26214400
    sudo sysctl -w net.core.rmem_default=26214400
    

  2. Reduce packet rate:

    # Add small delays between packets
    for i in range(1000):
        await send_packet()
        await asyncio.sleep(0.001)  # 1ms delay
    

  3. Check CPU usage:

    # Monitor while running tests
    htop  # or top on macOS/Linux
    

Platform-Specific Issues

Windows Issues

Problem: Tests fail only on Windows

Common issues:

  1. Event loop policy:

    # Add to conftest.py or test setup
    import sys
    import asyncio
    
    if sys.platform == 'win32':
        asyncio.set_event_loop_policy(
            asyncio.WindowsProactorEventLoopPolicy()
        )
    

  2. Firewall prompts:

  3. Windows Defender may prompt to allow Python
  4. Allow for private networks
  5. Or use 127.0.0.1 binding (no firewall prompt)

  6. Path separators:

    # Use pathlib for cross-platform paths
    from pathlib import Path
    
    config_path = Path(__file__).parent / "config.yaml"
    

  7. Line endings:

  8. Git may convert line endings (CRLF vs LF)
  9. Configure .gitattributes:
    *.py text eol=lf
    

macOS Issues

Problem: Tests fail only on macOS

Common issues:

  1. Firewall blocks UDP:
  2. System Preferences → Security & Privacy → Firewall
  3. Click "Firewall Options"
  4. Add Python and allow incoming connections

  5. Too many open files:

    # Check limit
    ulimit -n
    
    # Increase limit
    ulimit -n 4096
    

  6. Gatekeeper blocking Python:

    # If Python was downloaded (not from App Store)
    xattr -d com.apple.quarantine /path/to/python
    

Linux Issues

Problem: Tests fail only on Linux

Common issues:

  1. Port binding requires root (ports < 1024):

    # Solution: Use ports >= 1024
    server = EmulatedLifxServer([device], "127.0.0.1", 56700)
    

  2. Too many open files:

    # Check limits
    ulimit -n
    cat /proc/sys/fs/file-max
    
    # Increase limit temporarily
    ulimit -n 4096
    
    # Increase permanently (add to /etc/security/limits.conf)
    * soft nofile 4096
    * hard nofile 8192
    

  3. SELinux or AppArmor restrictions:

    # Check SELinux
    getenforce
    
    # Check AppArmor
    sudo aa-status
    
    # May need to configure policies for network access
    

WSL (Windows Subsystem for Linux) Issues

Problem: Issues specific to WSL

Common issues:

  1. Port forwarding:
  2. WSL2 uses virtual network
  3. Ports may not be accessible from Windows
  4. Workaround: Use 0.0.0.0 binding and Windows firewall rules

  5. Network latency:

  6. WSL2 has slight network overhead
  7. May need longer timeouts

  8. File permissions:

  9. Files on Windows filesystem (e.g., /mnt/c/) have weird permissions
  10. Use Linux filesystem (e.g., ~/projects/)

Logging and Debugging Techniques

Enable Verbose Logging

Basic logging:

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

Emulator-specific logging:

# Enable only emulator logs
logging.getLogger('lifx_emulator').setLevel(logging.DEBUG)

CLI verbose mode:

lifx-emulator --verbose

Output:

RX: GetService (2) from ('127.0.0.1', 54321)
TX: StateService (3) to ('127.0.0.1', 54321)
RX: GetColor (101) from ('127.0.0.1', 54321) target=d073d5000001
TX: StateColor (107) to ('127.0.0.1', 54321)

Inspect Device State

During tests:

@pytest.fixture
async def emulator():
    device = create_color_light("d073d5000001")
    server = EmulatedLifxServer([device], "127.0.0.1", 56700)
    async with server:
        yield server, device  # Expose device for inspection

async def test_color_change(emulator):
    server, device = emulator

    # ... send SetColor command ...

    # Inspect device state
    print(f"Device color: {device.state.color}")
    print(f"Device power: {device.state.power_level}")
    assert device.state.color.hue == expected_hue

Packet Capture with Wireshark/tcpdump

Capture UDP packets:

# Linux/macOS - tcpdump
sudo tcpdump -i lo -n udp port 56700 -X

# Wireshark
# Filter: udp.port == 56700

Analyze LIFX packets: - First 36 bytes: LIFX header - Bytes 32-33: Packet type (little-endian) - Remaining bytes: Payload

Use pytest Debugging

Drop into debugger on failure:

pytest --pdb

Drop into debugger on first failure:

pytest -x --pdb

Set breakpoint in test:

async def test_something(emulator):
    device = ...
    breakpoint()  # Python 3.7+
    # or
    import pdb; pdb.set_trace()

Enable pytest output:**

# Show print() output
pytest -s

# Show test names as they run
pytest -v

# Both
pytest -sv

Common Error Messages

"RuntimeError: Server not running"

Cause: Attempting to use server before starting

Fix:

# Wrong
server = EmulatedLifxServer([device], "127.0.0.1", 56700)
await send_command()  # Server not started!

# Right
server = EmulatedLifxServer([device], "127.0.0.1", 56700)
async with server:
    await send_command()  # Server is running

"ValueError: Invalid serial format"

Cause: Serial number not 12 hex characters

Fix:

# Wrong
device = create_color_light("123")
device = create_color_light("d073d500001")  # 11 chars

# Right
device = create_color_light("d073d5000001")  # 12 chars

"TypeError: Object of type 'bytes' is not JSON serializable"

Cause: Trying to serialize device state with bytes

Context: Usually happens with custom serialization

Fix:

import base64

# Convert bytes to base64 string
serial_str = base64.b64encode(device.state.serial_bytes).decode('ascii')

"SyntaxError: 'await' outside async function"

Cause: Using async operations outside async context

Fix:

# Wrong
def test_device():
    await server.start()  # Can't await in sync function

# Right
async def test_device():
    await server.start()  # Now it works

# Or use asyncio.run()
def test_device():
    asyncio.run(async_main())

"DeprecationWarning: There is no current event loop"

Cause: Python 3.10+ changed event loop behavior

Fix:

# In conftest.py
import pytest
import asyncio

@pytest.fixture(scope="session")
def event_loop():
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()

Getting Help

If you're still stuck after trying these solutions:

  1. Check existing issues: https://github.com/Djelibeybi/lifx-emulator/issues
  2. Search documentation: Use the search feature in the docs
  3. Ask in discussions: https://github.com/Djelibeybi/lifx-emulator/discussions
  4. File a bug report: https://github.com/Djelibeybi/lifx-emulator/issues/new

When reporting issues, include: - Python version (python --version) - Operating system (Linux/macOS/Windows, version) - lifx-emulator version (pip show lifx-emulator) - Minimal reproduction code - Error messages and stack traces - What you've already tried

See Also