Skip to content

Continuing a fork while its sibling is alive returns HTTP 500: state entitlement charged for a shared long anchor #43

Description

@alphastorm

Symptom

On one session, with small prompts and no pressure, no restart and no concurrency:

base -> turn -> {sibling A, sibling B} -> continue B

the last request answers HTTP 500 sequence StateImage entitlement is inconsistent (a std::logic_error from ProgramImplCore::reserve_state_entitlement). Reproduced in 40 s against the published v0.6.3-qwen38-5090-beta.1 image (ghcr.io/alphastorm/ninfer-runtime@sha256:fc244576…) at 140-token prompts, deterministically, 2/2 attempts.

This is the ordinary two-subagent fanout shape: two agents branch from one turn and one of them keeps going.

Cause

reserve_state_entitlement(sequence, slots) compares the plan's granted demand.active_entitlement.device.state_slots against state_footprint(sequence), and the footprint counted every device-resident image the sequence referenced, including the long anchor it inherited from its parent (5de10f3a, "a sibling fork inherits the long anchor it forks from"). Two siblings reference one image; the store counts it once. Whichever sibling ran next was therefore charged for the residency the other sibling's turn had caused, footprint > slots, and the request threw.

Deleting one sibling first releases the shared anchor, which is why the existing agent-protocol gate (fork two siblings, delete one, continue the survivor) never exercised it.

Scope

Present since the sibling anchor inheritance shipped, i.e. every release from v0.6.2 onward on the RTX 5090 container lane; the native lanes carry the same runtime path. Not caused by the upstream-2026-09 backport - none of its commits touch the state path.

Fix

Charge the entitlement for the device-resident images exclusive to the sequence, using the same state_exclusive_to_sequence predicate the rest of the state accounting uses. Exclusivity is the question the entitlement asks; the materialization residency guard (#37) asks the opposite one, which is why one fix is not the other.

Upstream reaches the same conclusion in b8786751 ("fix(runtime): correct aliased state ownership") via a 356-line ownership rework with a new state-binding model; this is the narrow equivalent.

Regression coverage

The RTX 5090 profile gate's agent-protocol phase now continues a sibling while the other sibling is still alive, before the delete sequence, and records live_sibling_continuation_status. A standalone reproduction lives in omp-ninfer at scripts/sibling_continue_probe.py.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions