{"record":{"id":"a70734361ac898e9","repo":"agalwood/Motrix","slug":"aria2-addtorrent-gid-must-contain-exactly-16-hexad","errorCode":null,"errorMessage":"aria2 addTorrent gid must contain exactly 16 hexadecimal characters","messagePattern":"aria2 addTorrent gid must contain exactly 16 hexadecimal characters","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/core/engine/aria2/aria2-adapter.ts","lineNumber":388,"sourceCode":"      throw err\n    }\n    const raw = opts['bt-tracker']\n    if (!raw) return []\n    return raw\n      .split(',')\n      .map((s) => s.trim())\n      .filter((s) => s.length > 0)\n  }\n\n  async getGlobalStats(): Promise<GlobalStats> {\n    const raw = await this.rpc.getGlobalStat()\n    return translateGlobalStat(raw)\n  }\n\n  async addTorrent(params: AddTorrentParams): Promise<string> {\n    const extraGid = params.extraEngineOptions?.gid\n    if (extraGid !== undefined && typeof extraGid !== 'string') {\n      throw new TypeError(\n        'aria2 addTorrent gid must contain exactly 16 hexadecimal characters'\n      )\n    }\n    const requestedGid =\n      params.gid ?? (typeof extraGid === 'string' ? extraGid : undefined)\n    const opts: Record<string, string> = {\n      dir: params.saveDir,\n      pause: String(params.pause ?? false),\n    }\n    if (requestedGid !== undefined) {\n      if (!/^[0-9a-fA-F]{16}$/.test(requestedGid)) {\n        throw new TypeError(\n          'aria2 addTorrent gid must contain exactly 16 hexadecimal characters'\n        )\n      }\n    }\n    if (params.selectedFiles?.length) {\n      opts['select-file'] = params.selectedFiles.join(',')","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/engine/aria2/aria2-adapter.ts#L370-L406","documentation":"Thrown as a TypeError by Aria2Adapter.addTorrent when params.extraEngineOptions?.gid is defined but is not a string (e.g. it's an array or number). This is a type-safety guard: extraEngineOptions is typed as Record<string, string | string[]>, so a gid could arrive as a string[] from the engine-agnostic passthrough. The adapter requires gid to be a single string before it can be validated against the 16-hex pattern.","triggerScenarios":"addTorrent is called with extraEngineOptions containing a gid key whose value is a string array (or any non-string type), e.g. { gid: ['abc123'] } passed through the shell-supplied options passthrough.","commonSituations":"A plugin or shell layer passes engine options as arrays (valid for multi-value aria2 options like header) but includes gid as an array; a serialization layer converts single values to arrays; incorrect typing at a boundary that constructs AddTorrentParams.","solutions":["Ensure extraEngineOptions.gid is a single string, not an array — use params.gid directly instead of extraEngineOptions.gid for reserved GIDs","If constructing options programmatically, coerce gid to a string before passing: String(gid)","Use the dedicated params.gid field (typed as string) rather than the generic extraEngineOptions passthrough"],"exampleFix":"// before\nawait adapter.addTorrent({ ..., extraEngineOptions: { gid: ['0000000000000001'] } })\n// after\nawait adapter.addTorrent({ ..., gid: '0000000000000001' })","handlingStrategy":"type-guard","validationCode":"function normalizeGid(gid: unknown): string | undefined {\n  if (gid === undefined) return undefined\n  if (typeof gid !== 'string') {\n    throw new TypeError('gid must be a string')\n  }\n  return gid\n}\n// Use params.gid directly instead of extraEngineOptions.gid","typeGuard":"function isStringGid(value: unknown): value is string {\n  return typeof value === 'string'\n}","tryCatchPattern":"try {\n  await adapter.addTorrent(params)\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('gid must contain')) {\n    // Fix the gid type and retry\n    params.gid = String(params.extraEngineOptions?.gid ?? '')\n    delete params.extraEngineOptions?.gid\n    await adapter.addTorrent(params)\n  } else throw e\n}","preventionTips":["Use the dedicated params.gid field (typed as string) rather than extraEngineOptions.gid","Never pass gid as an array — coerce to string at the boundary where options are constructed"],"tags":["aria2","torrent","gid","type-safety","validation"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}