paperclipai/paperclip · error

steering is unavailable

Error message

steering is unavailable

What it means

steer forwards a mid-turn user message to the underlying HarnessSession's optional steer capability. When the session driver does not implement steer (this.#session.steer === undefined), the backend throws "steering is unavailable" because the driver cannot inject messages into an in-flight turn. The call is wrapped with protocol-integrity checks on entry and exit.

Source

Thrown at packages/paperclip-runner/src/backends/harness-driver-backend.ts:682

    this.#assertProtocolIntegrity();
    try {
      const started = await this.#session.startTurn(input);
      this.#assertProtocolIntegrity();
      return started;
    } catch (error) {
      this.#rethrowProtocolIntegrity(error);
      throw error;
    }
  }

  steer(input: {
    turnId: string;
    message: { role: "user"; text: string };
    correlationId?: string;
  }) {
    this.#assertProtocolIntegrity();
    if (this.#session.steer === undefined)
      throw new Error("steering is unavailable");
    return this.#withProtocolIntegrity(() => this.#session.steer!(input));
  }

  interrupt(input: { turnId?: string; reason?: string }) {
    if (this.#session.interrupt === undefined)
      throw new Error("interruption is unavailable");
    return this.#session.interrupt(input);
  }

  cancel(input: { reason: string; signal: AbortSignal }) {
    if (input.signal.aborted) {
      throw (
        input.signal.reason ?? new Error("native session cancellation aborted")
      );
    }
    // This flag is the adapter's synchronous publication boundary. Provider
    // interruption happens afterward as passive cleanup, so a slow or broken
    // transport cannot synthesize or publish new accepted output for the turn.

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the driver implements steer before enabling steering in the UI/CLI for that agent type
  2. Upgrade the adapter/driver to a version with steer support
  3. Queue the message and deliver it as the next turn's user message instead of steering
  4. Feature-detect: if (!session.steer) fall back to interrupt-then-send

Example fix

// before
await backend.steer({ turnId, message });
// after
if (!backend.canSteer()) {
  await backend.interrupt({ reason: 'pending-steer' });
  await backend.send({ message });
} else {
  await backend.steer({ turnId, message });
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof session.steer !== 'function') throw new Error('steering not supported by this driver');

Type guard

const canSteer = (s: HarnessSession): s is HarnessSession & { steer: NonNullable<HarnessSession['steer']> } => typeof s.steer === 'function';

Try / catch

try { await backend.steer({ turnId, message }); } catch (e) { if (e.message === 'steering is unavailable') { queueForNextTurn(message); } else throw e; }

Prevention

When it happens

Trigger: Calling steer() while a turn is running on a session whose driver lacks a steer implementation; steering a driver/adapter that never supported mid-turn steering (session.steer undefined).

Common situations: Using an agent adapter (e.g., a non-interactive CLI driver) that cannot accept input mid-run; steering before upgrading to a driver version with steer support; UI sending a steer action for an agent type that only accepts turn-boundary messages.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/0a0c2be89d81a251. Report an issue: GitHub.