agalwood/Motrix · error · AppError

EngineProtocolError

EngineProtocolError

Error message

Engine returned non-numeric history count: ${result.count}

What it means

Thrown as an AppError (code EngineProtocolError) by Aria2Adapter.getHistoryCount when the RPC method getDownloadResultCount returns a result whose count field cannot be parsed into a finite integer via Number.parseInt. This indicates the aria2 engine (or the Motrix fork's sqlite3 persistence layer) returned an unexpected response shape — a protocol violation rather than a user error.

Source

Thrown at src/core/engine/aria2/aria2-adapter.ts:583

    return [...active, ...waiting].map((t) => ({
      gid: t.gid,
      infoHash: t.infoHash || undefined,
    }))
  }

  async listStopped(): Promise<Array<{ gid: string; infoHash?: string }>> {
    const stopped = await this.rpc.tellStopped(0, 1000, ['gid', 'infoHash'])
    return stopped.map((t) => ({
      gid: t.gid,
      infoHash: t.infoHash || undefined,
    }))
  }

  async getHistoryCount(filter?: HistoryFilter): Promise<number> {
    const result = await this.rpc.getDownloadResultCount(filter)
    const n = Number.parseInt(result.count, 10)
    if (!Number.isFinite(n)) {
      throw new AppError(
        ErrorCode.EngineProtocolError,
        `Engine returned non-numeric history count: ${result.count}`
      )
    }
    return n
  }

  async searchHistory(
    query: HistorySearchQuery,
    offset: number,
    num: number
  ): Promise<DownloadTask[]> {
    const rows = await this.rpc.searchDownloadResult(query, offset, num)
    return rows.map(translateRawToTask)
  }

  async exportSession(filePath: string): Promise<void> {
    await this.rpc.exportSession(filePath)

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Verify the aria2 binary is the Motrix fork version that supports getDownloadResultCount (check the feature report from probe())
  2. Restart the aria2 engine to clear any corrupted in-memory state
  3. Check engine logs for the raw RPC response that produced the non-numeric count
  4. If using a custom or older aria2 build, fall back to tellStopped length estimation instead of getDownloadResultCount
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await adapter.getHistoryCount(filter)
} catch (e) {
  if (e instanceof AppError && e.code === ErrorCode.EngineProtocolError) {
    // Engine returned malformed data — restart and retry, or fall back
    log.error({ err: e }, 'history count protocol error, returning 0')
    return 0
  }
  throw e
}

Prevention

When it happens

Trigger: this.rpc.getDownloadResultCount(filter) returns an object whose .count is undefined, null, an empty string, or a non-numeric string; Number.parseInt(result.count, 10) produces NaN, which fails Number.isFinite().

Common situations: Aria2 fork version mismatch where getDownloadResultCount is not supported or returns a different shape; a corrupted sqlite3 history database causing the engine to return an error string instead of a count; RPC response deserialization bug; the engine process crashed mid-response and returned a partial JSON.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/db6af62a2c94edb1. Report an issue: GitHub.