Skip to content

Upgrade to Python 3.13 and fix libtmux locale issue#2092

Draft
neubig wants to merge 2 commits intomainfrom
fix/python313-locale-for-libtmux
Draft

Upgrade to Python 3.13 and fix libtmux locale issue#2092
neubig wants to merge 2 commits intomainfrom
fix/python313-locale-for-libtmux

Conversation

@neubig
Copy link
Contributor

@neubig neubig commented Feb 16, 2026

Summary

The libtmux compatibility issue with PyInstaller (issue #1886) was caused by missing locale environment variables, not a race condition in libtmux.

Root Cause

When LC_ALL/LANG are not set in PyInstaller builds:

  1. tmux falls back to ASCII/C locale
  2. The UTF-8 separator character (U+241E) gets converted to underscore _
  3. libtmux's format parsing fails because it can't split on the separator
  4. TmuxObjectDoesNotExist is raised

The Fix

Simply add locale environment variables to the Dockerfile:

ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8

Test Results

Configuration Result
Original libtmux 0.53.0 + NO locale fix + PyInstaller 0/20 passed
Original libtmux 0.53.0 + LC_ALL=C.UTF-8 + PyInstaller 100/100 passed

Changes

  • Upgrade base images from Python 3.12 to Python 3.13
  • Add LC_ALL=C.UTF-8 and LANG=C.UTF-8 to Dockerfile
  • Update workflow files to use Python 3.13

What This Replaces

This is a clean replacement for PR #1978, which had accumulated workaround code that is no longer needed:

  • ❌ No need to pin libtmux to a custom fork
  • ❌ No need for _get_session_id_directly() workaround
  • ❌ No retry logic needed in tmux_terminal.py

Closes #1886

@neubig can click here to continue refining the PR


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.13-nodejs22 Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:35afa79-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-35afa79-python \
  ghcr.io/openhands/agent-server:35afa79-python

All tags pushed for this build

ghcr.io/openhands/agent-server:35afa79-golang-amd64
ghcr.io/openhands/agent-server:35afa79-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:35afa79-golang-arm64
ghcr.io/openhands/agent-server:35afa79-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:35afa79-java-amd64
ghcr.io/openhands/agent-server:35afa79-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:35afa79-java-arm64
ghcr.io/openhands/agent-server:35afa79-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:35afa79-python-amd64
ghcr.io/openhands/agent-server:35afa79-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-amd64
ghcr.io/openhands/agent-server:35afa79-python-arm64
ghcr.io/openhands/agent-server:35afa79-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-arm64
ghcr.io/openhands/agent-server:35afa79-golang
ghcr.io/openhands/agent-server:35afa79-java
ghcr.io/openhands/agent-server:35afa79-python

About Multi-Architecture Support

  • Each variant tag (e.g., 35afa79-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 35afa79-python-amd64) are also available if needed

The libtmux compatibility issue with PyInstaller was caused by missing
locale environment variables, not a race condition. When LC_ALL/LANG
are not set, tmux converts UTF-8 separator characters to underscores,
breaking libtmux's format parsing.

Changes:
- Upgrade base images from Python 3.12 to Python 3.13
- Add LC_ALL=C.UTF-8 and LANG=C.UTF-8 to Dockerfile
- Update workflow files to use Python 3.13

This allows using the standard libtmux package from PyPI without any
workarounds or custom forks.

Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig added the test-examples Run all applicable "examples/" files. Expensive operation. label Feb 16, 2026 — with OpenHands AI
Ruff now simplifies Generator[X, None, None] to Generator[X] for py313.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 16, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/agent
   base.py1891989%200, 289, 293–297, 345–347, 357, 367, 375–376, 486, 523–524, 534–535
TOTAL18977522372% 

@github-actions
Copy link
Contributor

github-actions bot commented Feb 16, 2026

🔄 Running Examples with openhands/claude-haiku-4-5-20251001

Generated: 2026-02-16 04:40:31 UTC

Example Status Duration Cost
01_standalone_sdk/02_custom_tools.py ✅ PASS 24.4s $0.03
01_standalone_sdk/03_activate_skill.py ✅ PASS 18.7s $0.02
01_standalone_sdk/05_use_llm_registry.py ✅ PASS 13.3s $0.01
01_standalone_sdk/07_mcp_integration.py ✅ PASS 35.3s $0.03
01_standalone_sdk/09_pause_example.py ✅ PASS 15.5s $0.01
01_standalone_sdk/10_persistence.py ✅ PASS 27.2s $0.02
01_standalone_sdk/11_async.py ✅ PASS 32.0s $0.04
01_standalone_sdk/12_custom_secrets.py ✅ PASS 17.9s $0.02
01_standalone_sdk/13_get_llm_metrics.py ✅ PASS 21.4s $0.02
01_standalone_sdk/14_context_condenser.py ✅ PASS 3m 39s $0.24
01_standalone_sdk/17_image_input.py ✅ PASS 17.0s $0.02
01_standalone_sdk/18_send_message_while_processing.py ✅ PASS 16.2s $0.01
01_standalone_sdk/19_llm_routing.py ✅ PASS 13.5s $0.02
01_standalone_sdk/20_stuck_detector.py ✅ PASS 19.3s $0.03
01_standalone_sdk/21_generate_extraneous_conversation_costs.py ✅ PASS 10.7s $0.00
01_standalone_sdk/22_anthropic_thinking.py ✅ PASS 16.9s $0.01
01_standalone_sdk/23_responses_reasoning.py ✅ PASS 40.0s $0.01
01_standalone_sdk/24_planning_agent_workflow.py ✅ PASS 4m 46s $0.40
01_standalone_sdk/25_agent_delegation.py ❌ FAIL
Timed out after 600 seconds
10m 0s $0.16
01_standalone_sdk/26_custom_visualizer.py ✅ PASS 17.6s $0.03
01_standalone_sdk/28_ask_agent_example.py ✅ PASS 40.3s $0.04
01_standalone_sdk/29_llm_streaming.py ✅ PASS 32.5s $0.03
01_standalone_sdk/30_tom_agent.py ✅ PASS 26.6s $0.02
01_standalone_sdk/31_iterative_refinement.py ✅ PASS 6m 15s $0.42
01_standalone_sdk/32_configurable_security_policy.py ✅ PASS 17.7s $0.02
01_standalone_sdk/34_critic_example.py ❌ FAIL
Exit code 1
3.8s --
01_standalone_sdk/36_event_json_to_openai_messages.py ✅ PASS 9.2s $0.00
01_standalone_sdk/37_llm_profile_store.py ✅ PASS 3.9s $0.00
01_standalone_sdk/38_browser_session_recording.py ❌ FAIL
Timed out after 600 seconds
10m 0s $0.03
02_remote_agent_server/01_convo_with_local_agent_server.py ✅ PASS 1m 21s $0.07
02_remote_agent_server/02_convo_with_docker_sandboxed_server.py ✅ PASS 1m 12s $0.03
02_remote_agent_server/03_browser_use_with_docker_sandboxed_server.py ✅ PASS 59.9s $0.09
02_remote_agent_server/04_convo_with_api_sandboxed_server.py ✅ PASS 1m 18s $0.01
02_remote_agent_server/07_convo_with_cloud_workspace.py ✅ PASS 22.5s $0.02
02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py ✅ PASS 3m 13s $0.01
04_llm_specific_tools/01_gpt5_apply_patch_preset.py ✅ PASS 21.0s $0.02
04_llm_specific_tools/02_gemini_file_tools.py ✅ PASS 44.9s $0.07
05_skills_and_plugins/01_loading_agentskills/main.py ✅ PASS 11.3s $0.01
05_skills_and_plugins/02_loading_plugins/main.py ✅ PASS 7.2s $0.01

❌ Some tests failed

Total: 39 | Passed: 36 | Failed: 3 | Total Cost: $2.00

Failed examples:

  • examples/01_standalone_sdk/25_agent_delegation.py: Timed out after 600 seconds
  • examples/01_standalone_sdk/34_critic_example.py: Exit code 1
  • examples/01_standalone_sdk/38_browser_session_recording.py: Timed out after 600 seconds

View full workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test-examples Run all applicable "examples/" files. Expensive operation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Docker/Apptainer sandboxed server example tests failing in CI

2 participants