Skip to content

apr run --gpu: a refused GPU attempt redoes the whole generation on CPU, then refuses it (exit 14) — refuse before the redo #4969

Open

Description

@noahgift

A forced apr run --gpu whose GPU attempt is refused still runs the whole generation on the CPU, then refuses the result (exit 14). The user waits for an answer the command then refuses to give.

Evidence

RTX 4090, apr 0.70.2 (743f739020) (the #3602 leg-2 branch), qwen2.5-coder-0.5b-instruct-q4_k_m.gguf, --prompt Hi --max-tokens 1 --json, 2026-10-09:

run rc wall generation_ms rejected_ms backend
--gpu, healthy 0 13.3 s 91 null ran gpu, fell_back false
--gpu, refused (planted: CUDA_VISIBLE_DEVICES= empty) 14 12.9 s 11550 19 ran cpu, fell_back true
--no-gpu 0 11.5 s 10706 null ran cpu

The refused run's stderr ends with error: Backend unavailable: gpu was forced and selected, but its runtime attempt on this model failed and the generation ran on CPU. Refusing to report that as success ... (R-0b, #3002 ...). Its stdout still carries the CPU answer as JSON. So 11.5 s of the 12.9 s went to a CPU generation that the command then refused. (On this build the CPU path is slow because of the Q5_0 reference cost #3602 leg 1 fixes. The waste scales with whatever the CPU generation costs.)

Why

The refusal (#3002 R-0b) is right and stays. It runs in reconcile_accelerator (apr-cli run_entry.rs), which is after generation. The engine already knows the run was forced: InferenceConfig.accel_forced gates the wgpu fallback in run_gguf_generate. But after a refused CUDA or wgpu attempt it goes straight on to the CPU loop.

#3602 done_when 4 was decided by a quorum round (2/3): forced --gpu fails and an unforced run falls back, both naming the reason. The dissenting lane voted to fail fast inside #3602. The majority filed it here instead, as its own change.

done_when

  1. A forced run (--gpu, or --backend naming a GPU) whose GPU attempt is refused exits 14 before any CPU forward. It names the reason: the F2 cosine and position for a correctness refusal, or the device error. It prints no CPU answer.
  2. An unforced run is unchanged. It falls back, exits 0, gives the reason on stderr, and reports rejected_ms in --json.
  3. Falsifier: the planted refused attempt above exits 14 with no CPU generation (generation_ms absent, and wall time well under the --no-gpu time). A healthy --gpu run is unchanged, and so is a bare apr run with the same plant (rc 0, fell_back true).

Refs #3602 #3002

🤖 Generated with Claude Code

Activity

  1. noahgift commented on Oct 14, 2026

    @noahgift
    MemberAuthor

    merged into #5312 (master ticket for epic #3994; triage rule 7b, 2026-10-14). It is a checklist row there; reopen this issue when work on it starts or its PR opens.

  2. noahgift commented on Oct 14, 2026

    @noahgift
    MemberAuthor

    Moved: this sub-ticket is now a row in #5325 (apr on GPU: refuse by name before OOM, never fall back to CPU silently), split out of #5312 by theme.

  3. added this to the 0.73.0 milestone on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions