{"record":{"id":"db6af62a2c94edb1","repo":"agalwood/Motrix","slug":"engineprotocolerror","errorCode":"EngineProtocolError","errorMessage":"Engine returned non-numeric history count: ${result.count}","messagePattern":"Engine returned non-numeric history count: (.+?)","errorType":"error_code","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/engine/aria2/aria2-adapter.ts","lineNumber":583,"sourceCode":"    return [...active, ...waiting].map((t) => ({\n      gid: t.gid,\n      infoHash: t.infoHash || undefined,\n    }))\n  }\n\n  async listStopped(): Promise<Array<{ gid: string; infoHash?: string }>> {\n    const stopped = await this.rpc.tellStopped(0, 1000, ['gid', 'infoHash'])\n    return stopped.map((t) => ({\n      gid: t.gid,\n      infoHash: t.infoHash || undefined,\n    }))\n  }\n\n  async getHistoryCount(filter?: HistoryFilter): Promise<number> {\n    const result = await this.rpc.getDownloadResultCount(filter)\n    const n = Number.parseInt(result.count, 10)\n    if (!Number.isFinite(n)) {\n      throw new AppError(\n        ErrorCode.EngineProtocolError,\n        `Engine returned non-numeric history count: ${result.count}`\n      )\n    }\n    return n\n  }\n\n  async searchHistory(\n    query: HistorySearchQuery,\n    offset: number,\n    num: number\n  ): Promise<DownloadTask[]> {\n    const rows = await this.rpc.searchDownloadResult(query, offset, num)\n    return rows.map(translateRawToTask)\n  }\n\n  async exportSession(filePath: string): Promise<void> {\n    await this.rpc.exportSession(filePath)","sourceCodeStart":565,"sourceCodeEnd":601,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/engine/aria2/aria2-adapter.ts#L565-L601","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","solutions":["Verify the aria2 binary is the Motrix fork version that supports getDownloadResultCount (check the feature report from probe())","Restart the aria2 engine to clear any corrupted in-memory state","Check engine logs for the raw RPC response that produced the non-numeric count","If using a custom or older aria2 build, fall back to tellStopped length estimation instead of getDownloadResultCount"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await adapter.getHistoryCount(filter)\n} catch (e) {\n  if (e instanceof AppError && e.code === ErrorCode.EngineProtocolError) {\n    // Engine returned malformed data — restart and retry, or fall back\n    log.error({ err: e }, 'history count protocol error, returning 0')\n    return 0\n  }\n  throw e\n}","preventionTips":["Ensure the aria2 binary is the Motrix fork that supports getDownloadResultCount (check featureReport from probe())","Monitor engine RPC responses for unexpected shapes and restart the engine if corruption is detected","Keep the aria2 fork up to date to avoid sqlite3 persistence protocol mismatches"],"tags":["aria2","rpc","protocol-error","history","engine"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}