Skip to content

fix(sdk): make ACP auth failures self-diagnosing - #4404

Merged
simonrosenberg merged 4 commits into
mainfrom
acp-codex-auth-observability
Aug 7, 2026
Merged

fix(sdk): make ACP auth failures self-diagnosing#4404
simonrosenberg merged 4 commits into
mainfrom
acp-codex-auth-observability

Conversation

@simonrosenberg

@simonrosenberg simonrosenberg commented Aug 6, 2026

Copy link
Copy Markdown
Member

HUMAN:

Improve error logs for codex acp


AGENT:

Why

Codex ACP auth-selection failures currently collapse missing auth material, invalid auth JSON, and missing API keys into the same warning. The subprocess stderr is also never consumed, while non-protocol stdout is debug-only, leaving hangs and crashes without actionable diagnostics. This blocked the investigation documented in OpenHands/enterprise#120.

Summary

  • Report whether Codex auth.json is missing or invalid and whether both API-key variables are unset.
  • Drain ACP subprocess stderr and log non-JSON stdout at info level, with secret redaction and a 4,000-character per-line limit.
  • Cover the warning content and subprocess stream logging with focused tests.

Issue Number

https://github.com/OpenHands/enterprise/issues/120

How to Test

Run uv run pytest tests/sdk/agent/test_acp_agent.py. The full ACP agent module passes: 436 passed in 10.86s. The stream tests feed fake stdout/stderr readers through the same async logging functions used by spawned ACP processes and assert the resulting info-level records.

Run uv run pre-commit run --files openhands-sdk/openhands/sdk/agent/acp_agent.py tests/sdk/agent/test_acp_agent.py. Ruff formatting/lint, pycodestyle, Pyright, import rules, and tool registration all pass.

A live codex-acp binary is not required because this PR changes SDK-side stream draining and logging only; protocol JSON remains connected to the ACP client unchanged.

Video/Screenshots

Not applicable; this is backend observability with no visual change.

Type

  • Bug fix
  • Feature
  • Refactor
  • Breaking change
  • Docs / chore

Notes

This is logging-only. Subprocess diagnostics are redacted before logging and individual lines are truncated to bound Datadog event size.


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-slim 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:a106bc1-python

Run

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

All tags pushed for this build

ghcr.io/openhands/agent-server:a106bc1-golang-amd64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-golang-amd64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-golang-amd64
ghcr.io/openhands/agent-server:a106bc1-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:a106bc1-golang-arm64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-golang-arm64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-golang-arm64
ghcr.io/openhands/agent-server:a106bc1-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:a106bc1-java-amd64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-java-amd64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-java-amd64
ghcr.io/openhands/agent-server:a106bc1-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:a106bc1-java-arm64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-java-arm64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-java-arm64
ghcr.io/openhands/agent-server:a106bc1-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:a106bc1-python-amd64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-python-amd64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-python-amd64
ghcr.io/openhands/agent-server:a106bc1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-amd64
ghcr.io/openhands/agent-server:a106bc1-python-arm64
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-python-arm64
ghcr.io/openhands/agent-server:acp-codex-auth-observability-python-arm64
ghcr.io/openhands/agent-server:a106bc1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim-arm64
ghcr.io/openhands/agent-server:a106bc1-golang
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-golang
ghcr.io/openhands/agent-server:acp-codex-auth-observability-golang
ghcr.io/openhands/agent-server:a106bc1-golang_tag_1.21-bookworm
ghcr.io/openhands/agent-server:a106bc1-java
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-java
ghcr.io/openhands/agent-server:acp-codex-auth-observability-java
ghcr.io/openhands/agent-server:a106bc1-eclipse-temurin_tag_17-jdk
ghcr.io/openhands/agent-server:a106bc1-python
ghcr.io/openhands/agent-server:a106bc158a108112b4c2dbda3df2ba5fd590ea12-python
ghcr.io/openhands/agent-server:acp-codex-auth-observability-python
ghcr.io/openhands/agent-server:a106bc1-nikolaik_s_python-nodejs_tag_python3.13-nodejs22-slim

About Multi-Architecture Support

  • Each variant tag (e.g., a106bc1-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., a106bc1-python-amd64) are also available if needed

Co-authored-by: openhands <openhands@all-hands.dev>
@all-hands-bot

Copy link
Copy Markdown
Collaborator

👋 This PR needs a couple of things fixed before OpenHands can review it:

  • the PR description's HUMAN: section needs at least 20 characters describing what you tested, not just the template placeholder

Push an update once this is addressed and this check re-runs automatically.

This is an automated check - no AI was used to generate this comment.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Python API breakage checks — ✅ PASSED

Result:PASSED

Action log

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

REST API breakage checks (OpenAPI) — ✅ PASSED

Result:PASSED

Action log

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/agent
   acp_agent.py154113691%399, 569, 575, 658, 698, 917–919, 976–977, 1057, 1077, 1308–1309, 1358, 1360, 1364, 1368, 1391, 1454–1455, 1460, 1527, 1804, 1816, 1829, 1897, 1900–1901, 1918–1919, 1935, 1952, 1957, 2065, 2070, 2119–2120, 2122, 2394, 2490, 2516–2517, 2533–2534, 2577–2578, 2792–2795, 2799–2801, 2804–2809, 2813–2815, 2819, 2823–2824, 2827–2828, 2831, 2833, 3092, 3107–3108, 3111–3113, 3121, 3125, 3149–3150, 3153, 3162–3163, 3201, 3206–3208, 3212–3213, 3245, 3282–3283, 3334, 3549–3551, 3554–3555, 3601, 3763, 3771–3773, 3816–3817, 3820, 3828–3830, 3837, 3839, 3843, 3846, 3855–3857, 3864, 3900–3901, 3919–3922, 3925, 3929–3931, 3933, 3937–3938, 4207–4208, 4221–4222, 4278–4279
TOTAL393861117872% 

@simonrosenberg simonrosenberg self-assigned this Aug 7, 2026
@simonrosenberg simonrosenberg added the review-this This label triggers a PR review by OpenHands label Aug 7, 2026

Copy link
Copy Markdown
Collaborator

🔍 Review in progress…

We are performing the review through OpenHands Cloud Automation. You can log in and view the conversation here.

@all-hands-bot

Copy link
Copy Markdown
Collaborator

🤖 OpenHands is reviewing this PR.

Head commit: c95fd51b941bd4a2f5ec05651ce63a7e39aece82
View the conversation: https://oss-agent-canvas.ngrok.dev/conversations/112c2034-9272-4817-97e3-533965afb4ca

This comment was posted by an AI agent (OpenHands).

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a focused, observability-only fix that addresses a real diagnostic gap: when the Codex ACP server offers chat-gpt and/or api-key auth methods but the SDK cannot supply matching credentials, the prior single-line warning gave operators no way to tell whether the codex auth.json was missing, malformed, or the wrong type — and stderr from the ACP subprocess was never drained at all, so hangs surfaced as silent -32603 Internal errors. The change reports each failure mode distinctly via _auth_selection_failure_reason, drains stderr into a redaction+truncation pipeline (_log_acp_subprocess_stderr), and promotes the existing non-JSON stdout path to INFO with the same redaction+truncation. Subprocess stream teardown is also covered: the new assert process.stderr is not None makes the post-Popen invariant explicit.

Two minor suggestions below — neither blocks merging.

Risk Assessment: 🟢 LOW — logging only, no control-flow or wire-protocol changes; secret redaction and per-line truncation are in place; new tests cover the warning content and stream logging.

Worth merging.

This review was generated by an AI agent (OpenHands) on behalf of the user through OpenHands Automation. View conversation

Comment thread openhands-sdk/openhands/sdk/agent/acp_agent.py
Comment thread tests/sdk/agent/test_acp_agent.py

all-hands-bot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Review complete.

This review was performed through OpenHands Cloud Automation. You can log in and view the conversation here.

@all-hands-bot all-hands-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review was created by an AI agent (OpenHands) on behalf of the repository maintainers.

Summary

This is a focused, logging-only improvement that makes Codex ACP auth-selection failures self-diagnosing and adds subprocess stderr draining. The changes are correct, well-scoped, and safe. No material issues found.

Risk: Low — No protocol, control-flow, or persistence changes. All new code paths are diagnostic logging with secret redaction and line truncation.

Findings

1. _auth_selection_failure_reason only diagnoses Codex methods (minor)

_select_auth_method handles five method types (chat-gpt, vertex-ai, oauth-personal, api-key, gemini-api-key), but _auth_selection_failure_reason only produces specific reasons for chat-gpt and api-key. If a server offers only gemini-api-key (with GEMINI_API_KEY unset) or vertex-ai (with GOOGLE_APPLICATION_CREDENTIALS missing), the warning falls through to the generic "no supported credential source is available" — which is exactly the kind of un-actionable message this PR set out to fix.

This is acceptable given the PR is explicitly scoped to the Codex/enterprise#120 investigation, but consider extending the reason builder to cover the other methods (or at least gemini-api-key) in a follow-up so the function name doesn't overpromise.

2. Stream-logging tests don't exercise redaction or truncation (minor test gap)

test_logs_non_jsonrpc_stdout and test_logs_subprocess_stderr verify the message content at INFO level, but don't assert that (a) a line exceeding 4,000 chars is truncated, or (b) a line containing a secret pattern (e.g. sk-...) is redacted. Both maybe_truncate and redact_text_secrets are independently tested utilities, so this is a coverage gap rather than a defect, but a single assertion per path would guard against wiring regressions (e.g., calling them in the wrong order, or forgetting to pass truncate_after).

3. Fire-and-forget stderr task (informational)

The stderr-drain task created at line 2728 is not stored — matching the existing pattern for the stdout filter task at line 2725. In CPython asyncio, create_task does not hold a strong reference; the caller must. In practice the task runs to completion because it only awaits readline() until EOF, but the modern guidance is to save the task reference (e.g. on self._stderr_task) so it can also be awaited/cancelled during _shutdown_runtime. This is a pre-existing pattern, not a regression introduced here.

4. Env-var-style secrets in subprocess output may not be fully redacted (minor security note)

redact_text_secrets targets dict/quote patterns (api_key='...', 'KEY': '...'), URL query params, and bare API-key literals from known providers (sk-..., xoxb-..., Bearer ...). If codex-acp or another ACP server logs an env-var-style secret like CODEX_API_KEY=<opaque-token> where the value doesn't match a known provider prefix, it would not be redacted. This is a residual best-effort limitation of the existing utility rather than a new issue, but worth being aware of given the PR explicitly moves stderr from debug to INFO level.

Verdict

The PR is safe to merge. The changes are correct, the redaction/truncation approach is reasonable, and the test coverage is adequate for the scope. The observations above are minor/informational and suitable for follow-up rather than blocking.

Comment thread openhands-sdk/openhands/sdk/agent/acp_agent.py
Comment thread openhands-sdk/openhands/sdk/agent/acp_agent.py
Comment thread tests/sdk/agent/test_acp_agent.py
Review feedback on #4404 (line 929): the stderr-drain task added by this
PR wasn't stored anywhere, so nothing held a strong reference to it or
cancelled it on shutdown — the same pre-existing gap as the stdout
filter task. Store both on self and cancel/await them in
_shutdown_runtime, mirroring how _process/_conn are already torn down.
@simonrosenberg
simonrosenberg merged commit c7e270a into main Aug 7, 2026
29 of 30 checks passed
@simonrosenberg
simonrosenberg deleted the acp-codex-auth-observability branch August 7, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-this This label triggers a PR review by OpenHands

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants