stablyai/orca · error

adb did not return a local scrcpy port

Error message

adb did not return a local scrcpy port

What it means

Thrown by ScrcpyStreamSession.deploy when a dynamic port forward was requested (this.port === 0) but the adb forward output could not be parsed as a finite positive integer. adb is expected to print the allocated local port number to stdout when forwarding to 'tcp:0'; anything else means the forward command misbehaved or the output format changed.

Source

Thrown at src/main/emulator/android/scrcpy-stream-session.ts:113

      throw error
    }
    return session
  }

  private async deploy(): Promise<void> {
    const { runner, sdk, serial, localJarPath } = this.options
    ensureAdbOk(
      await runner(sdk.adb, pushScrcpyServerArgs(serial, localJarPath, SCRCPY_DEVICE_JAR_PATH)),
      'scrcpy server push'
    )
    const forward = ensureAdbOk(
      await runner(sdk.adb, scrcpyForwardArgs(serial, this.port, this.scid)),
      'scrcpy port forward'
    )
    if (this.port === DYNAMIC_FORWARD_PORT) {
      const allocated = Number.parseInt(forward.stdout.trim(), 10)
      if (!Number.isFinite(allocated) || allocated <= 0) {
        throw new Error('adb did not return a local scrcpy port')
      }
      this.port = allocated
      emulatorProbe('scrcpy.forward.port', { serial, port: this.port })
    }
  }

  private spawnServer(): void {
    const { sdk, serial, maxSize } = this.options
    // The server is long-running, so spawn it directly rather than via the
    // request/response command runner.
    this.server = spawn(sdk.adb, startScrcpyServerArgs(serial, { scid: this.scid, maxSize }), {
      stdio: ['ignore', 'pipe', 'pipe']
    })
    let serverLog = ''
    const capture = (chunk: Buffer): void => {
      serverLog += chunk.toString()
    }
    this.server.stdout?.on('data', capture)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Update platform-tools to a version whose `adb forward tcp:0 ...` prints the allocated port to stdout.
  2. Pass an explicit localPort in ScrcpyStreamOptions to skip the dynamic allocation path entirely.
  3. Run `adb forward --list` and `adb forward tcp:0 tcp:<port>` manually to see what your adb prints and reconcile.
  4. Restart the adb server if its state is corrupt.

Example fix

// before: dynamic port, adb prints nothing usable
await ScrcpyStreamSession.start({ runner, sdk, serial, localJarPath }, cb)
// after: pin a local port
await ScrcpyStreamSession.start({ runner, sdk, serial, localJarPath, localPort: 27183 }, cb)
Defensive patterns

Strategy: fallback

Validate before calling

// Pin a local port to skip dynamic allocation entirely.
const opts: ScrcpyStreamOptions = { runner, sdk, serial, localJarPath, localPort: 27183 }

Type guard

function isDynamicPortError(e: unknown): e is Error {
  return e instanceof Error && /did not return a local scrcpy port/i.test(e.message)
}

Try / catch

try {
  return await ScrcpyStreamSession.start(opts, cb)
} catch (e) {
  if (isDynamicPortError(e)) {
    // fall back to a fixed local port
    return await ScrcpyStreamSession.start({ ...opts, localPort: 27183 }, cb)
  }
  throw e
}

Prevention

When it happens

Trigger: ScrcpyStreamSession.start called with no localPort (defaulting to DYNAMIC_FORWARD_PORT=0); the scrcpyForwardArgs adb invocation succeeded (ensureAdbOk passed) but forward.stdout.trim() does not parseInt to a positive number — e.g. empty output, an error string, or a newer adb printing differently.

Common situations: An older or newer adb version with different forward-output formatting; the forward succeeded structurally but printed a status line instead of a port; a locale/format quirk; adb server in an odd state returning empty stdout while exiting 0.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/d99afac901c82a9e. Report an issue: GitHub.