Skip to content

[Bug] codex_exec backend: rollouts skipped (calls=0) + bwrap sandbox blocks file reads despite danger-full-access #209

Description

Bug Description

SkillOpt v0.2.0 with codex_exec backend as both optimizer and target produces 0 model calls (calls=0, wall=0s) and skips all rollouts, resulting in all-zero scores. When rollouts DO execute (earlier runs), Codex's internal bwrap sandbox blocks file reads even with --sandbox danger-full-access.

Environment

  • SkillOpt: 0.2.0 (pip install from main branch)
  • Codex CLI: 0.144.6
  • Python: 3.13.5
  • OS: Linux 6.8.0-136-generic (Docker container, VPS)
  • Kernel: kernel.unprivileged_userns_clone = 1

Config

env: githubcodereview  # custom benchmark
optimizer_backend: codex_exec
target_backend: codex_exec
optimizer_model: gpt-5.5
target_model: gpt-5.5
sandbox: danger-full-access
codex_exec_full_auto: false
num_epochs: 2
batch_size: 2
sel_env_num: 3
test_env_num: 5
eval_test: true

Environment vars also set explicitly:

export CODEX_CLI_BIN=/opt/data/bin/codex
export CODEX_SANDBOX_MODE=danger-full-access
export CODEX_EXEC_FULL_AUTO=false
export CODEX_EXEC_SANDBOX=danger-full-access

Issue 1: Rollouts skipped entirely (calls=0, wall=0s)

Output from skillopt-train:

  [gate] metric=hard
  [slow update] acceptance=force-accept (unconditional)

============================================================
  BASELINE — evaluate initial skill on Selection set (valid_seen)
============================================================
  Selection items: 2
    [rollout] 1/2 id=cr_009 hard=0
    [rollout] 2/2 id=cr_003 hard=0
  [baseline result] selection hard=0.0000 soft=0.0000 gate[hard]=0.0000

  [skip] all 1 steps complete — jumping to evaluation

  [EPOCH 1/1] shuffled_seeds=[1043]

  [done] best skill from step 0, score=0.0000

Final summary shows 0 model calls:

  Final Summary
  steps=1 accept=0 reject=1 skip=0
  best_score=0.0000 (step 0)  wall=0s

  TEST scores:
    [1] init/baseline (S_0)          : test_hard=0.0000
    [2] best-on-val (step 0)         : test_hard=0.0000 test_soft=0.0000
    [3] final/last skill             : test_hard=0.0000 test_soft=0.0000
  total tokens: 0 (prompt=0 completion=0 calls=0)

The [skip] all 1 steps complete — jumping to evaluation message suggests the train loop thinks all steps are already done and skips the actual rollout/optimization phase.

Issue 2: bwrap sandbox blocks Codex file reads

In runs where rollouts DO execute (before the skip issue manifests), Codex returns sandbox-blocker messages instead of actual code reviews:

Sample assistant response (from conversation.json):

<answer>
I'm blocked by the execution environment before I can read `skillopt-target` or `task.md`.

The required shell command failed immediately with:

bwrap: No permissions to create a new namespace, likely because the kernel does not allow non-privileged user namespaces.

Because approval is set to `never`, I can't rerun outside the sandbox or change permissions.
</answer>

Key observation: Codex CLI works fine when called directly

codex exec --sandbox danger-full-access --skip-git-repo-check \
  --cd /workspace --model gpt-5.5 \
  --output-last-message /tmp/out.txt \
  "Read task.md and review the diff"

Works perfectly (reads files, returns review, 13k tokens used, 8s).

But when SkillOpt's codex_harness.py calls Codex via subprocess (with --full-auto or approval_policy=never), bwrap blocks file access.

CODEX_EXEC_FULL_AUTO=false does not help

Setting CODEX_EXEC_FULL_AUTO=false (env var) is correctly read by backend_config.py (verified via get_codex_exec_config()full_auto: false), but the bwrap error persists. The harness in codex_harness.py:_run_codex_cli_exec checks config.get("full_auto", True) — but even with full_auto=False, the --sandbox danger-full-access flag alone doesn't prevent bwrap from being invoked by Codex internally.

Expected Behavior

  1. Rollouts should not be skipped — SkillOpt should call the target model for each evaluation item.
  2. Codex with danger-full-access sandbox should be able to read workspace files (task.md, SKILL.md) without bwrap blocking.

Reproduction

  1. Install SkillOpt from main:

    uv venv .venv --python 3.13
    uv pip install git+https://github.com/microsoft/SkillOpt.git@main
  2. Create a custom benchmark env (or use any env with codex_exec backend).

  3. Run:

    export CODEX_CLI_BIN=$(which codex)
    export CODEX_EXEC_FULL_AUTO=false
    export CODEX_EXEC_SANDBOX=danger-full-access
    skillopt-train --config configs/custom.yaml --num_epochs 1
  4. Observe: calls=0, wall=0s, all scores 0.0.

Suspected Root Causes

Issue 1 (skip logic):

The [slow update] acceptance=force-accept (unconditional) combined with [skip] all 1 steps complete suggests an off-by-one or state-persistence bug in the epoch/step tracker. Possibly related to eval_test: true causing premature step completion.

Issue 2 (bwrap):

Codex CLI v0.144.6 enforces bwrap sandbox regardless of --sandbox danger-full-access when called via subprocess with --json/--ephemeral flags. SkillOpt's _run_codex_exec in codex_backend.py uses both flags. The _run_codex_cli_exec in codex_harness.py uses --full-auto by default which also triggers bwrap.

Workarounds Attempted

Setting Result
sandbox: danger-full-access (YAML) ❌ bwrap still blocks
CODEX_EXEC_FULL_AUTO=false (env) ❌ bwrap still blocks
CODEX_EXEC_SANDBOX=danger-full-access (env) ❌ bwrap still blocks
Direct codex exec --sandbox danger-full-access (manual) ✅ Works
openai_compatible backend (Gemini OpenAI endpoint) Not tested (API credits depleted)

Impact

Blocks the primary documented use case from the SkillOpt README: "optimize with frontier models, deploy on cheap models" using Codex CLI as the target agent. Custom benchmark environments cannot be evaluated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions