{"record":{"id":"d4828bcc456dc650","repo":"pinpoint-apm/pinpoint","slug":"either-webhookid-or-ruleid-is-needed-to-get-webhoo","errorCode":null,"errorMessage":"Either webhookId or ruleId is needed to get webhook send information","messagePattern":"Either webhookId or ruleId is needed to get webhook send information","errorType":"validation","errorClass":"ResponseStatusException","httpStatus":400,"severity":"error","filePath":"webhook/src/main/java/com/navercorp/pinpoint/web/webhook/controller/WebhookSendInfoController.java","lineNumber":63,"sourceCode":"        }\n        String webhookSendInfoId = webhookSendInfoService.insertWebhookSendInfo(webhookSendInfo);\n        return new WebhookSendInfoResponse(Result.SUCCESS, webhookSendInfoId);\n    }\n\n    @DeleteMapping()\n    public Response deleteWebhookSendInfo(@RequestBody WebhookSendInfo webhookSendInfo) {\n        if (!StringUtils.hasText(webhookSendInfo.getWebhookSendInfoId())) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"there should be webhookSendInfoId to delete webhook\");\n        }\n        webhookSendInfoService.deleteWebhookSendInfo(webhookSendInfo);\n        return SimpleResponse.ok();\n    }\n\n    @GetMapping()\n    public List<WebhookSendInfo> getWebhookSendInfo(@RequestParam(value=WEBHOOK_ID, required=false) String webhookId,\n                                                    @RequestParam(value=RULE_ID, required=false) String ruleId) {\n        if (!StringUtils.hasText(webhookId) && !StringUtils.hasText(ruleId)) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"Either webhookId or ruleId is needed to get webhook send information\");\n        }\n\n        if (StringUtils.hasText(webhookId)) {\n            return webhookSendInfoService.selectWebhookSendInfoByWebhookId(webhookId);\n        }\n\n        return webhookSendInfoService.selectWebhookSendInfoByRuleId(ruleId);\n    }\n\n    @PutMapping()\n    public Response updateWebhookSendInfo(@RequestBody WebhookSendInfo webhookSendInfo) {\n         if (!StringUtils.hasText(webhookSendInfo.getWebhookSendInfoId()) ||\n                !StringUtils.hasText(webhookSendInfo.getWebhookId()) || !StringUtils.hasText(webhookSendInfo.getRuleId())) {\n            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, \"There should be webhookSendInfoId, webhookId and ruleId to update webhook send information\");\n        }\n        webhookSendInfoService.updateWebhookSendInfo(webhookSendInfo);\n        return SimpleResponse.ok();\n    }","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/webhook/src/main/java/com/navercorp/pinpoint/web/webhook/controller/WebhookSendInfoController.java#L45-L81","documentation":"The GET endpoint for webhook send information supports two optional query parameters, webhookId and ruleId, but requires at least one of them. If both are absent or blank the controller throws a 400 BAD_REQUEST ResponseStatusException, because it cannot determine which send-info records to select.","triggerScenarios":"Calling GET on the webhook send-info endpoint with no query parameters, or with both webhookId= and ruleId= present but empty/whitespace.","commonSituations":"Clients build the URL from state where both filter values are undefined/empty strings (e.g. a cleared search form); a query-string builder drops falsy params entirely; consumers expect an unfiltered list endpoint but this one intentionally has no list-all mode.","solutions":["Pass webhookId as a query parameter to fetch send-info for a specific webhook: GET ?webhookId=<id>.","Pass ruleId instead when filtering by rule: GET ?ruleId=<id>.","Client-side: only call the endpoint after validating that at least one of webhookId/ruleId is non-empty.","If listing all is intended, use the webhook or rule list endpoints and iterate."],"exampleFix":"// before\nconst list = await api.get('/webhookSendInfo');\n// after\nconst list = await api.get(`/webhookSendInfo?webhookId=${encodeURIComponent(webhookId)}`);","handlingStrategy":"validation","validationCode":"if (!(webhookId?.trim() || ruleId?.trim())) {\n  throw new Error('Either webhookId or ruleId is required to fetch webhook send information');\n}","typeGuard":"const hasWebhookFilter = (q: { webhookId?: string; ruleId?: string }): q is { webhookId: string } | { ruleId: string } =>\n  Boolean(q.webhookId?.trim() || q.ruleId?.trim());","tryCatchPattern":"try {\n  const list = await fetchWebhookSendInfo({ webhookId });\n} catch (e) {\n  if (e?.status === 400) {\n    console.warn('Missing webhookId/ruleId filter; skipping webhook send info fetch');\n    return [];\n  }\n  throw e;\n}","preventionTips":["Build the query string from validated state; drop the call entirely when no filter is selected.","Remember this endpoint has no list-all mode — always supply webhookId or ruleId.","Use URLSearchParams and skip empty values to avoid ?webhookId= producing a blank parameter.","Write an API-client wrapper that enforces the filter before sending."],"tags":["rest-api","bad-request","query-parameters","webhook"],"backgroundTag":"missing-required-argument","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}