matrix-org / matrix-org/matrix-hookshot
Creating a GitLab Issue Room Fails with "issue_iid is invalid"
- Dominant language
- TypeScript
- Stars
- 450
- Forks
- 95
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 23
Description
**Describe the bug**
After setting a personal token for my GitLab instance in a DM room with Hookshot, I try to run `gitlab open issue` to create an issue room, but the command fails. The bot sends "Failed to handle command: undefined" and in the log this appears:
```
matrix-hookshot-1 | INFO 11:18:19:647 [AdminRoom] Looking up issue INSTANCENAME PROJECTNAME#NaN
matrix-hookshot-1 | 172.30.0.5 - - [03/Jul/2022:11:18:19 +0000] "PUT /transactions/289995?access_token=REDACTED HTTP/1.1" 200 2 "-" "Synapse/1.61.1"
matrix-hookshot-1 | WARN 11:18:19:666 [GitLabClient] Failed to get issue: Error: Request failed with status code 400
matrix-hookshot-1 | at createError (/bin/matrix-hookshot/node_modules/axios/lib/core/createError.js:16:15)
matrix-hookshot-1 | at settle (/bin/matrix-hookshot/node_modules/axios/lib/core/settle.js:17:12)
matrix-hookshot-1 | at IncomingMessage.handleStreamEnd (/bin/matrix-hookshot/node_modules/axios/lib/adapters/http.js:293:11)
matrix-hookshot-1 | at IncomingMessage.emit (node:events:539:35)
matrix-hookshot-1 | at endReadableNT (node:internal/streams/readable:1345:12)
matrix-hookshot-1 | at processTicksAndRejections (node:internal/process/task_queues:83:21) {
matrix-hookshot-1 | config: {
matrix-hookshot-1 | transitional: {
matrix-hookshot-1 | silentJSONParsing: true,
matrix-hookshot-1 | forcedJSONParsing: true,
matrix-hookshot-1 | clarifyTimeoutError: false
matrix-hookshot-1 | },
matrix-hookshot-1 | adapter: [Function: httpAdapter],
matrix-hookshot-1 | transformRequest: [ [Function: transformRequest] ],
matrix-hookshot-1 | transformResponse: [ [Function: transformResponse] ],
matrix-hookshot-1 | timeout: 0,
matrix-hookshot-1 | xsrfCookieName: 'XSRF-TOKEN',
matrix-hookshot-1 | xsrfHeaderName: 'X-XSRF-TOKEN',
matrix-hookshot-1 | maxContentLength: -1,
matrix-hookshot-1 | maxBodyLength: -1,
matrix-hookshot-1 | validateStatus: [Function: validateStatus],
matrix-hookshot-1 | headers: {
matrix-hookshot-1 | Accept: 'application/json, text/plain, */*',
matrix-hookshot-1 | Authorization: 'Bearer REDACTED',
matrix-hookshot-1 | 'User-Agent': 'matrix-hookshot/1.7.3 (+https://github.com/matrix-org/matrix-hookshot)'
matrix-hookshot-1 | },
matrix-hookshot-1 | baseURL: 'GITLABURL',
matrix-hookshot-1 | method: 'get',
matrix-hookshot-1 | url: 'api/v4/ONLYGROUPNAME/issues/NaN',
matrix-hookshot-1 | data: undefined
matrix-hookshot-1 | },
matrix-hookshot-1 | request: ClientRequest {
matrix-hookshot-1 | _events: [Object: null prototype] {
matrix-hookshot-1 | abort: [Function (anonymous)],
matrix-hookshot-1 | aborted: [Function (anonymous)],
matrix-hookshot-1 | connect: [Function (anonymous)],
matrix-hookshot-1 | error: [Function (anonymous)],
matrix-hookshot-1 | socket: [Function (anonymous)],
matrix-hookshot-1 | timeout: [Function (anonymous)],
matrix-hookshot-1 | prefinish: [Function: requestOnPrefinish]
matrix-hookshot-1 | },
matrix-hookshot-1 | _eventsCount: 7,
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | outputData: [],
matrix-hookshot-1 | outputSize: 0,
matrix-hookshot-1 | writable: true,
matrix-hookshot-1 | destroyed: false,
matrix-hookshot-1 | _last: true,
matrix-hookshot-1 | chunkedEncoding: false,
matrix-hookshot-1 | shouldKeepAlive: false,
matrix-hookshot-1 | maxRequestsOnConnectionReached: false,
matrix-hookshot-1 | _defaultKeepAlive: true,
matrix-hookshot-1 | useChunkedEncodingByDefault: false,
matrix-hookshot-1 | sendDate: false,
matrix-hookshot-1 | _removedConnection: false,
matrix-hookshot-1 | _removedContLen: false,
matrix-hookshot-1 | _removedTE: false,
matrix-hookshot-1 | _contentLength: 0,
matrix-hookshot-1 | _hasBody: true,
matrix-hookshot-1 | _trailer: '',
matrix-hookshot-1 | finished: true,
matrix-hookshot-1 | _headerSent: true,
matrix-hookshot-1 | _closed: false,
matrix-hookshot-1 | socket: TLSSocket {
matrix-hookshot-1 | _tlsOptions: [Object],
matrix-hookshot-1 | _secureEstablished: true,
matrix-hookshot-1 | _securePending: false,
matrix-hookshot-1 | _newSessionPending: false,
matrix-hookshot-1 | _controlReleased: true,
matrix-hookshot-1 | secureConnecting: false,
matrix-hookshot-1 | _SNICallback: null,
matrix-hookshot-1 | servername: 'MYGITLABINSTANCE',
matrix-hookshot-1 | alpnProtocol: false,
matrix-hookshot-1 | authorized: true,
matrix-hookshot-1 | authorizationError: null,
matrix-hookshot-1 | encrypted: true,
matrix-hookshot-1 | _events: [Object: null prototype],
matrix-hookshot-1 | _eventsCount: 10,
matrix-hookshot-1 | connecting: false,
matrix-hookshot-1 | _hadError: false,
matrix-hookshot-1 | _parent: null,
matrix-hookshot-1 | _host: 'MYGITLABINSTANCE',
matrix-hookshot-1 | _readableState: [ReadableState],
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | _writableState: [WritableState],
matrix-hookshot-1 | allowHalfOpen: false,
matrix-hookshot-1 | _sockname: null,
matrix-hookshot-1 | _pendingData: null,
matrix-hookshot-1 | _pendingEncoding: '',
matrix-hookshot-1 | server: undefined,
matrix-hookshot-1 | _server: null,
matrix-hookshot-1 | ssl: [TLSWrap],
matrix-hookshot-1 | _requestCert: true,
matrix-hookshot-1 | _rejectUnauthorized: true,
matrix-hookshot-1 | parser: null,
matrix-hookshot-1 | _httpMessage: [Circular *1],
matrix-hookshot-1 | [Symbol(res)]: [TLSWrap],
matrix-hookshot-1 | [Symbol(verified)]: true,
matrix-hookshot-1 | [Symbol(pendingSession)]: null,
matrix-hookshot-1 | [Symbol(async_id_symbol)]: 749,
matrix-hookshot-1 | [Symbol(kHandle)]: [TLSWrap],
matrix-hookshot-1 | [Symbol(lastWriteQueueSize)]: 0,
matrix-hookshot-1 | [Symbol(timeout)]: null,
matrix-hookshot-1 | [Symbol(kBuffer)]: null,
matrix-hookshot-1 | [Symbol(kBufferCb)]: null,
matrix-hookshot-1 | [Symbol(kBufferGen)]: null,
matrix-hookshot-1 | [Symbol(kCapture)]: false,
matrix-hookshot-1 | [Symbol(kSetNoDelay)]: false,
matrix-hookshot-1 | [Symbol(kSetKeepAlive)]: false,
matrix-hookshot-1 | [Symbol(kSetKeepAliveInitialDelay)]: 0,
matrix-hookshot-1 | [Symbol(kBytesRead)]: 0,
matrix-hookshot-1 | [Symbol(kBytesWritten)]: 0,
matrix-hookshot-1 | [Symbol(connect-options)]: [Object],
matrix-hookshot-1 | [Symbol(RequestTimeout)]: undefined
matrix-hookshot-1 | },
matrix-hookshot-1 | _header: 'GET /api/v4/ONLYGROUPNAME/issues/NaN HTTP/1.1\r\n' +
matrix-hookshot-1 | 'Accept: application/json, text/plain, */*\r\n' +
matrix-hookshot-1 | 'Authorization: Bearer REDACTED\r\n' +
matrix-hookshot-1 | 'User-Agent: matrix-hookshot/1.7.3 (+https://github.com/matrix-org/matrix-hookshot)\r\n' +
matrix-hookshot-1 | 'Host: MYGITLABINSTANCE\r\n' +
matrix-hookshot-1 | 'Connection: close\r\n' +
matrix-hookshot-1 | '\r\n',
matrix-hookshot-1 | _keepAliveTimeout: 0,
matrix-hookshot-1 | _onPendingData: [Function: nop],
matrix-hookshot-1 | agent: Agent {
matrix-hookshot-1 | _events: [Object: null prototype],
matrix-hookshot-1 | _eventsCount: 2,
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | defaultPort: 443,
matrix-hookshot-1 | protocol: 'https:',
matrix-hookshot-1 | options: [Object: null prototype],
matrix-hookshot-1 | requests: [Object: null prototype] {},
matrix-hookshot-1 | sockets: [Object: null prototype],
matrix-hookshot-1 | freeSockets: [Object: null prototype] {},
matrix-hookshot-1 | keepAliveMsecs: 1000,
matrix-hookshot-1 | keepAlive: false,
matrix-hookshot-1 | maxSockets: Infinity,
matrix-hookshot-1 | maxFreeSockets: 256,
matrix-hookshot-1 | scheduling: 'lifo',
matrix-hookshot-1 | maxTotalSockets: Infinity,
matrix-hookshot-1 | totalSocketCount: 1,
matrix-hookshot-1 | maxCachedSessions: 100,
matrix-hookshot-1 | _sessionCache: [Object],
matrix-hookshot-1 | [Symbol(kCapture)]: false
matrix-hookshot-1 | },
matrix-hookshot-1 | socketPath: undefined,
matrix-hookshot-1 | method: 'GET',
matrix-hookshot-1 | maxHeaderSize: undefined,
matrix-hookshot-1 | insecureHTTPParser: undefined,
matrix-hookshot-1 | path: '/api/v4/ONLYGROUPNAME/NaN',
matrix-hookshot-1 | _ended: true,
matrix-hookshot-1 | res: IncomingMessage {
matrix-hookshot-1 | _readableState: [ReadableState],
matrix-hookshot-1 | _events: [Object: null prototype],
matrix-hookshot-1 | _eventsCount: 3,
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | socket: [TLSSocket],
matrix-hookshot-1 | httpVersionMajor: 1,
matrix-hookshot-1 | httpVersionMinor: 1,
matrix-hookshot-1 | httpVersion: '1.1',
matrix-hookshot-1 | complete: true,
matrix-hookshot-1 | rawHeaders: [Array],
matrix-hookshot-1 | rawTrailers: [],
matrix-hookshot-1 | aborted: false,
matrix-hookshot-1 | upgrade: false,
matrix-hookshot-1 | url: '',
matrix-hookshot-1 | method: null,
matrix-hookshot-1 | statusCode: 400,
matrix-hookshot-1 | statusMessage: 'Bad Request',
matrix-hookshot-1 | client: [TLSSocket],
matrix-hookshot-1 | _consuming: false,
matrix-hookshot-1 | _dumped: false,
matrix-hookshot-1 | req: [Circular *1],
matrix-hookshot-1 | responseUrl: 'https://MYGITLABINSTANCE/api/v4/ONLYGROUPNAME/issues/NaN',
matrix-hookshot-1 | redirects: [],
matrix-hookshot-1 | [Symbol(kCapture)]: false,
matrix-hookshot-1 | [Symbol(kHeaders)]: [Object],
matrix-hookshot-1 | [Symbol(kHeadersCount)]: 24,
matrix-hookshot-1 | [Symbol(kTrailers)]: null,
matrix-hookshot-1 | [Symbol(kTrailersCount)]: 0,
matrix-hookshot-1 | [Symbol(RequestTimeout)]: undefined
matrix-hookshot-1 | },
matrix-hookshot-1 | aborted: false,
matrix-hookshot-1 | timeoutCb: null,
matrix-hookshot-1 | upgradeOrConnect: false,
matrix-hookshot-1 | parser: null,
matrix-hookshot-1 | maxHeadersCount: null,
matrix-hookshot-1 | reusedSocket: false,
matrix-hookshot-1 | host: 'MYGITLABINSTANCE',
matrix-hookshot-1 | protocol: 'https:',
matrix-hookshot-1 | _redirectable: Writable {
matrix-hookshot-1 | _writableState: [WritableState],
matrix-hookshot-1 | _events: [Object: null prototype],
matrix-hookshot-1 | _eventsCount: 2,
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | _options: [Object],
matrix-hookshot-1 | _ended: true,
matrix-hookshot-1 | _ending: true,
matrix-hookshot-1 | _redirectCount: 0,
matrix-hookshot-1 | _redirects: [],
matrix-hookshot-1 | _requestBodyLength: 0,
matrix-hookshot-1 | _requestBodyBuffers: [],
matrix-hookshot-1 | _onNativeResponse: [Function (anonymous)],
matrix-hookshot-1 | _currentRequest: [Circular *1],
matrix-hookshot-1 | _currentUrl: 'https://MYGITLABINSTANCE/api/v4/ONLYGROUPNAME/issues/NaN',
matrix-hookshot-1 | [Symbol(kCapture)]: false
matrix-hookshot-1 | },
matrix-hookshot-1 | [Symbol(kCapture)]: false,
matrix-hookshot-1 | [Symbol(kNeedDrain)]: false,
matrix-hookshot-1 | [Symbol(corked)]: 0,
matrix-hookshot-1 | [Symbol(kOutHeaders)]: [Object: null prototype] {
matrix-hookshot-1 | accept: [Array],
matrix-hookshot-1 | authorization: [Array],
matrix-hookshot-1 | 'user-agent': [Array],
matrix-hookshot-1 | host: [Array]
matrix-hookshot-1 | }
matrix-hookshot-1 | },
matrix-hookshot-1 | response: {
matrix-hookshot-1 | status: 400,
matrix-hookshot-1 | statusText: 'Bad Request',
matrix-hookshot-1 | headers: {
matrix-hookshot-1 | 'cache-control': 'no-cache',
matrix-hookshot-1 | 'content-length': '32',
matrix-hookshot-1 | 'content-type': 'application/json',
matrix-hookshot-1 | date: 'Sun, 03 Jul 2022 11:18:19 GMT',
matrix-hookshot-1 | server: 'nginx',
matrix-hookshot-1 | 'strict-transport-security': 'max-age=15552000',
matrix-hookshot-1 | vary: 'Origin',
matrix-hookshot-1 | 'x-content-type-options': 'nosniff',
matrix-hookshot-1 | 'x-frame-options': 'SAMEORIGIN',
matrix-hookshot-1 | 'x-request-id': '01G71WGSR4C7S5VCCSQYJJA8N7',
matrix-hookshot-1 | 'x-runtime': '0.008226',
matrix-hookshot-1 | connection: 'close'
matrix-hookshot-1 | },
matrix-hookshot-1 | config: {
matrix-hookshot-1 | transitional: [Object],
matrix-hookshot-1 | adapter: [Function: httpAdapter],
matrix-hookshot-1 | transformRequest: [Array],
matrix-hookshot-1 | transformResponse: [Array],
matrix-hookshot-1 | timeout: 0,
matrix-hookshot-1 | xsrfCookieName: 'XSRF-TOKEN',
matrix-hookshot-1 | xsrfHeaderName: 'X-XSRF-TOKEN',
matrix-hookshot-1 | maxContentLength: -1,
matrix-hookshot-1 | maxBodyLength: -1,
matrix-hookshot-1 | validateStatus: [Function: validateStatus],
matrix-hookshot-1 | headers: [Object],
matrix-hookshot-1 | baseURL: 'https://MYGITLABINSTANCE',
matrix-hookshot-1 | method: 'get',
matrix-hookshot-1 | url: 'api/v4/projects/ONLYGROUPNAME/NaN',
matrix-hookshot-1 | data: undefined
matrix-hookshot-1 | },
matrix-hookshot-1 | request: ClientRequest {
matrix-hookshot-1 | _events: [Object: null prototype],
matrix-hookshot-1 | _eventsCount: 7,
matrix-hookshot-1 | _maxListeners: undefined,
matrix-hookshot-1 | outputData: [],
matrix-hookshot-1 | outputSize: 0,
matrix-hookshot-1 | writable: true,
matrix-hookshot-1 | destroyed: false,
matrix-hookshot-1 | _last: true,
matrix-hookshot-1 | chunkedEncoding: false,
matrix-hookshot-1 | shouldKeepAlive: false,
matrix-hookshot-1 | maxRequestsOnConnectionReached: false,
matrix-hookshot-1 | _defaultKeepAlive: true,
matrix-hookshot-1 | useChunkedEncodingByDefault: false,
matrix-hookshot-1 | sendDate: false,
matrix-hookshot-1 | _removedConnection: false,
matrix-hookshot-1 | _removedContLen: false,
matrix-hookshot-1 | _removedTE: false,
matrix-hookshot-1 | _contentLength: 0,
matrix-hookshot-1 | _hasBody: true,
matrix-hookshot-1 | _trailer: '',
matrix-hookshot-1 | finished: true,
matrix-hookshot-1 | _headerSent: true,
matrix-hookshot-1 | _closed: false,
matrix-hookshot-1 | socket: [TLSSocket],
matrix-hookshot-1 | _header: 'GET /api/v4/projects/ONLYGROUPNAME/issues/NaN HTTP/1.1\r\n' +
matrix-hookshot-1 | 'Accept: application/json, text/plain, */*\r\n' +
matrix-hookshot-1 | 'Authorization: Bearer REDACTED\r\n' +
matrix-hookshot-1 | 'User-Agent: matrix-hookshot/1.7.3 (+https://github.com/matrix-org/matrix-hookshot)\r\n' +
matrix-hookshot-1 | 'Host: MYGITLABINSTANCE\r\n' +
matrix-hookshot-1 | 'Connection: close\r\n' +
matrix-hookshot-1 | '\r\n',
matrix-hookshot-1 | _keepAliveTimeout: 0,
matrix-hookshot-1 | _onPendingData: [Function: nop],
matrix-hookshot-1 | agent: [Agent],
matrix-hookshot-1 | socketPath: undefined,
matrix-hookshot-1 | method: 'GET',
matrix-hookshot-1 | maxHeaderSize: undefined,
matrix-hookshot-1 | insecureHTTPParser: undefined,
matrix-hookshot-1 | path: '/api/v4/projects/ONLYGROUPNAME/issues/NaN',
matrix-hookshot-1 | _ended: true,
matrix-hookshot-1 | res: [IncomingMessage],
matrix-hookshot-1 | aborted: false,
matrix-hookshot-1 | timeoutCb: null,
matrix-hookshot-1 | upgradeOrConnect: false,
matrix-hookshot-1 | parser: null,
matrix-hookshot-1 | maxHeadersCount: null,
matrix-hookshot-1 | reusedSocket: false,
matrix-hookshot-1 | host: 'MYGITLABINSTANCE',
matrix-hookshot-1 | protocol: 'https:',
matrix-hookshot-1 | _redirectable: [Writable],
matrix-hookshot-1 | [Symbol(kCapture)]: false,
matrix-hookshot-1 | [Symbol(kNeedDrain)]: false,
matrix-hookshot-1 | [Symbol(corked)]: 0,
matrix-hookshot-1 | [Symbol(kOutHeaders)]: [Object: null prototype]
matrix-hookshot-1 | },
matrix-hookshot-1 | data: { error: 'issue_iid is invalid' }
matrix-hookshot-1 | },
matrix-hookshot-1 | isAxiosError: true,
matrix-hookshot-1 | toJSON: [Function: toJSON]
matrix-hookshot-1 | }
```
**To Reproduce**
1. Set a token for a GitLab instance with Hookshot.
2. Send `gitlab open issue PROJECT`
**Expected behavior**
An issue room is created.
I'm running the latest Docker image, with this config:
```
gitlab:
instances:
ALIAS:
url: GITURL
webhook:
secret: SECRET
publicUrl: WEBHOOKURL
userIdPrefix: _gitlab_
```
From looking at the logged error, hookshot tries to get an issue via the API by the name of "NaN", which fails for obvious reasons. Also, instead of looking for calling the API with the full project path, it only seems to use the group, so the URLs logged are e.g. /api/v4/projects/ONLYGROUPNAME/issues/NaN instead of /api/v4/projects/GROUPNAME/PROJECTNAME/issues/1 which would expect.
Contributor guide
Research direction
Start at the handler for the `gitlab open issue` command and follow the `AdminRoom` and `GitLabClient` flow shown in the logs. Reproduce the command with a personal GitLab token and trace why the issue IID becomes `NaN`; done means the command performs a valid GitLab issue lookup and creates the issue room instead of returning an undefined error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- gitlab, typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100