Repository navigation
Description
What happened
Antigravity provider sessions fail to open on Linux; T3 shows it as an authentication problem. Real cause: the official ACP server raises SIGILL during startup, before answering the ACP initialize request.
Diagnosis
GDB on official ACP 1.4.1 (AVX2-capable CPU): SIGILL at ud2 (bytes 0f 0b) in colossus::Feature::Feature+2171, an absl DieBecauseNull trap - NOT an unsupported SIMD instruction; no AVX-512 at the faulting RIP. CPU-affinity dependent in a controlled A/B (official 1.4.5, 12 interleaved runs per condition, otherwise identical): normal affinity 4/12 init OK, 8/12 SIGILL; taskset -c 0-2 12/12 OK, 0 SIGILL (observed sample). Same crash on ACP 1.3.0/1.4.1/1.4.3/1.4.5. Hypothesis only (not established): internal startup ordering race near the DefaultEventManager worker.
Steps to reproduce
Use the official Linux x86-64 ACP 1.4.5 release from dl.google.com. Extract both binaries and run this from the directory containing agy_acp_server.par. Requires timeout (coreutils) and taskset (util-linux). No OAuth or real T3 profile is required for this ACP initialize probe.
ACP="$PWD/agy_acp_server.par"
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}}'
run_probe() (
label="$1"
shift
TH="$(mktemp -d)" || exit 1
trap 'rm -rf -- "$TH"' EXIT
# Delay initialize for 8 s to reproduce the measured startup window.
( sleep 8; printf '%s\n' "$INIT"; sleep 6 ) |
env -i PATH=/usr/bin:/bin GEMINI_HOME="$TH" \
AGY_ACP_FORCE_FILE_STORAGE=1 PYTHONUNBUFFERED=1 TMPDIR="$TH" \
timeout -s TERM -k 2s 25s "$@"
printf '%s: exit status %s\n' "$label" "$?"
)
# Condition A — normal CPU affinity.
run_probe normal "$ACP" --uid=
# Condition B — only CPUs 0-2; all other variables unchanged.
run_probe limited taskset -c 0-2 "$ACP" --uid=Repeat the A/B pair at least 10 times (12 interleaved runs per condition in our measurements) and count successful JSON-RPC initialize responses versus SIGILL / exit 132. This is a probabilistic failure: normal affinity produced 4/12 successful initializations and 8/12 SIGILL; CPUs 0-2 produced 12/12 successful initializations and 0 SIGILL in the observed sample. Ensure CPUs 0-2 are available in your environment. The first condition may also succeed on individual runs.
To inspect the faulting instruction, use GDB on the same official executable (handle SIGILL stop print nopass, run, p/x $rip, x/12i $rip). The detailed ud2 observation below was captured on ACP 1.4.1.
Version
T3 Code server v0.1.5 (systemd service). ACP runtimes tested separately: managed 1.3.0, official 1.4.1/1.4.3/1.4.5 (workaround verified on 1.4.5). T3 main pins ACP 1.4.1; v0.1.6 notes do not mention this failure.
Environment
Linux x86-64 (Debian 13) in a Proxmox LXC; Intel i5-10500, 12 logical CPUs, AVX2 yes / AVX-512 no; Node 22 for the sign-in helper. Official dl.google.com ACP binaries (1.4.5 archive sha256 923923a0d7dc8073f66cc6f78647a3107e3a7ee2b7e1a24df8b3927b5188013b). Seccomp/AppArmor ruled out (userspace ud2).
Evidence
GDB: SIGILL at colossus::Feature<bool>::Feature+2171; ud2, bytes 0f 0b; si_code=2 (ILL_ILLOPN). A/B (1.4.5, 12 interleaved runs each, identical except affinity): A normal affinity init_ok 4/12, SIGILL 8/12; B taskset -c 0-2 init_ok 12/12, SIGILL 0. #11414 already reports SIGILL on a no-AVX2 CPU; searches for ud2/colossus traps/affinity dependency return no other reports - this adds the AVX2-capable, ud2, affinity-dependent evidence.Related issues
#11414 - closest (SIGILL on a CPU lacking AVX2; closed by reporter after a T3 update): distinct because our CPU has AVX2 and the fault is a deliberate ud2 with an affinity dependency still present in binary 1.4.5. #13842/#16744 - SIGKILL seccomp/RLIMIT_RTTIME (different signal+cause). #23002 - NSS/group lookup (different stage). #25465 - refresh timeout; cross-ref only: each ACP launch unpacks ~1 GB into TMPDIR; no storage-performance claims made.
Fix applied or workaround
Custom executable path wrapper (providerInstances.antigravity.config.binaryPath): execs the official ACP 1.4.5 binary under taskset -c 0-2 (SIGILL mitigation; ANTIGRAVITY_ACP_CPUS overridable); TMPDIR redirected to private /dev/shm as hygiene only (startup-to-initialize ~6 s on this host with either tmpfs or disk; no storage-speed effect claimed). Official arguments untouched. Real T3 E2E acceptance passed: provider ready + 5 models, authenticated real model requests, file tools, two sessions, crash recovery, service restart.
Filed by
Prepared by Jorma, a Hermes Agent assistant (model: zai/glm-5.3-flash), on the machine operator's direction, in a Pi/oh-my-pstack-style workflow. The gdb/strace findings and A/B counts are real tool measurements; the race hypothesis and report text are the agent's interpretation. No LLM-generated code in the results.
Activity
mikesulsenti commentedon Nov 2, 2026
We hit the same crash and found what looks like the deciding factor: the CPU ID the runtime starts on, not timing. It crashes whenever the ID is at or above the number of CPUs the container shows. That would also explain why
taskset -c 0-2works.Environment
agy-acp-server-1.4.1-linux-x86_64.zip(sha2562e37012c…7871384, matches the hash T3 pins)Key observation
The LXC gets a sparse host cpuset, but lxcfs shows a smaller, renumbered CPU count:
The SIGILL report names CPU IDs that are ≥ 20 every time it gives one (
received by PID … on cpu 21,on cpu 27). Our stack trace differs from yours (absl::Uniform<>()←placer::common::FileSetManagerFactory::Options::Options()←placer::client::…InitFactory()←GoogleInitializer::RunInitializers()), but it is the same kind of deliberateud2trap during startup (si_code=2).A/B test
This is your
initializeprobe, with launches interleaved, official 1.4.1:1-2,5-11,15,17-21,24,26-28,31)1,2,5-11,15,17-19)20,21,24,26-28,31)Through the wrapper below, 7/7 more probes initialized, and after pointing T3 at the wrapper, the provider came up in T3 Code (confirmed by the owner in the UI).
Our guess (not verified in the binary): something in Google's startup code sizes a per-CPU table from the visible CPU count, then indexes it by the real CPU ID (
sched_getcpu()). When the ID is past the end of the table, a bounds or null check fires. On a host where CPU IDs are contiguous from 0, this never happens. That's probably why it mostly hits Proxmox/LXC users with acoreslimit, where the cpuset is often sparse. Your 4/12 success rate without pinning fits this: sometimes the process happens to land on a low ID.Workaround
This is the same approach as yours, but the CPU list is worked out at each launch: allowed CPUs with IDs below the visible count. That way it still works if the host reassigns the container's CPUs. Put the official
agy_acp_server.parandlocalharness_externalin one directory. Put this wrapper in a second directory, plus a symlink namedlocalharness_externalpointing at the real one (T3 checks that file is next to the executable's real path). Then set Custom ACP executable path to the wrapper. T3 picks up the change with no restart.Trade-offs: Antigravity and everything it starts (agent shell commands, builds) run on fewer CPUs (13 of 20 here), and T3 no longer updates the runtime automatically.
Possible fixes on the T3 side
chenydev commentedon Nov 10, 2026
Summary
On machines whose online CPU IDs are not contiguous from 0 (here some cores are offline on purpose),
agy_acp_server.par1.4.5 aborts at startup with SIGILL. It is not a missing CPU instruction. The server appears to size per-CPU data by the count of online CPUs but index it by CPU ID. With holes in the numbering, an index goes out of range and a bounds check traps withud2. T3 then reports the generic "The downloaded Antigravity runtime could not start in this environment."tasksetalone cannot fix this. I found a workaround that needs no root and does not change host CPU state (below).Environment
agy-acp-server-1.4.5-linux-x86_64.zip(sha256923923a0…5188013b, matches the pinned hash)Likely cause (inferred from the server's own crash output and the experiments below)
ud2trap insideeventmanager::TopologyHelper::InitStealOrders(), called fromEventManager2::EventManager2(). That is the pattern a hardened-libc++ bounds check produces, not an unsupported instruction. In the register dump the server prints, the loop index is 2 (an offline CPU here) and the looked-up value is-1, which looks like an index for a CPU missing from the topology.Check failed: num_cpus == t->NumCPUs() (16 vs. 12)at eventmanager2.cc:1186.concurrent::percpu::PerCpu<absl::InsecureBitGen>←colossus::Feature<bool>::Feature(). That fits per-CPU tables sized by the online count (12) being indexed by the current CPU ID (up to 15).So the server seems to assume that every CPU ID < number of online CPUs, and that this count equals the affinity mask size. Both are false when online CPU IDs have gaps.
Experiments (unprivileged user+mount namespace, bind-mounting files under
/sys/devices/system/cpu)taskset -c 0-1,4-9/taskset -c 12-15InitStealOrders(5/5 each)--eventmanager2_use_sys_topologyInitStealOrderscpuNdirs,possible/present= online listonline=1with a synthesizedtopology/Check failed: num_cpus == t->NumCPUs() (12 vs. 1)(eventmanager2.cc:1186)onlineshown as0-15Check failed: num_cpus == t->NumCPUs() (16 vs. 12); the topology appears to be capped by the affinity mask, and the kernel will not let you pin to offline CPUstaskset -c 0-1+onlineshown as0-1initializeOK 8/8; T3's validation checks all pass (agentInfoantigravity-acp/1.4.5, protocol 1,loadSession,resume,auth.logout,oauth-personal);authenticateproduces the Google sign-in URL through T3'sBROWSERhelper as expectedWith IDs
0-1,4-9,12-15, the only consistent view is a contiguous prefix of the online IDs starting at 0, which here is{0,1}.Workaround (no root; host CPU state unchanged)
Set these as T3's Custom ACP executable path (
.../agy_acp_server.par). Put them in a directory with the official runtime extracted to$AGY_REAL_RUNTIME.agy_acp_server.par:agy-cpu-view(private user+mount namespace; only/sys/devices/system/cpu/onlinechanges, the program still runs as your uid):localharness_external(a static Go binary that is not affected; this wrapper restores full affinity so agent tool runs are not limited to 2 cores):Costs: the ACP server process itself is limited to 2 CPUs, and you need unprivileged user namespaces. So far I have only verified
initializeand the start of the sign-in flow; full sessions are still to test.Corrections to my original comment
localharness_externalsibling, and T3 always passes--uid=on Linux. The nixpkgsagy acp(1.5.3) rejects--uid=and uses different session mode IDs (accept-edits;auto_edit/yoloare rejected), so it is not a drop-in replacement either.Suggested fixes
/sys/devices/system/cpu/possible,_NPROCESSORS_CONF) rather than the online count, or map CPU IDs to dense indexes. Also handle topology lookups that return -1 for CPUs not present in the topology (offline CPUs).SIGILL … InitStealOrders) in the "could not start in this environment" error. A documented hook for a launcher or wrapper around the managed runtime would also make workarounds like the one above easier.Original comment
Another data point: I hit the same "The downloaded Antigravity runtime could not start in this environment." on bare metal, and here
tasksetdoes not help. The crash seems tied to the CPU topology (offline cores) rather than to which CPU the process is scheduled on.taskset -c 0-1,4-9(IDs < nproc)taskset -c 12-15(IDs >= nproc)Interpretation at the time (narrowed down above): with non-contiguous online CPUs, something in
InitStealOrders()indexes by CPU ID or assumes the configured and online counts match. I did not bring the offline cores back online to confirm, since they are offline on purpose.derhub commentedon Nov 14, 2026
Another confirmed case: bare-metal Intel hybrid CPU, CPUs taken offline on purpose
agy-acp-server-1.4.5-linux-x86_64.zip(sha256 matches the pinned923923a0…5188013b)isolcpus=8,9is set:online: 0-7,10-31,offline: 8-9Install fails with "The downloaded Antigravity runtime could not start in this environment." The same crash also breaks Antigravity branch-name generation (
TextGenerationError: … Antigravity text generation failed←AcpProcessExitedError: ACP process exited after signal SIGILL). The stack trace is the one above:SIGILL si_code=2ineventmanager::TopologyHelper::InitStealOrders()←EventManager2::EventManager2().onlineshown as0-7plustaskset -c 0-7initializeOK 5/5With the wrapper set as the Antigravity Binary path, T3 picked it up without a restart and the provider shows ready (1.4.5, authenticated).
So this isn't specific to AMD or containers: any gap in the online CPU IDs triggers it. Users who turn off a degraded Raptor Lake core are likely to hit it. It would help if T3 detected an online CPU list with gaps when the runtime exits with SIGILL, and named that cause instead of showing the generic error.
Drafted by Claude Opus 5.5 (Claude Code) during
t3 triage, on the reporter's behalf; the measurements were taken on their machine.