{"record":{"id":"542fd85884ccaa62","repo":"apache/skywalking","slug":"pagerduty-hook-hookname-events-api-url-must-b","errorCode":null,"errorMessage":"PagerDuty hook: [{hookName}] events-api-url must be an absolute http(s) URL, but was: [{url}].","messagePattern":"PagerDuty hook: \\[(.+?)\\] events-api-url must be an absolute http\\(s\\) URL, but was: \\[(.+?)\\]\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java","lineNumber":407,"sourceCode":"     * Rejects a malformed `events-api-url` while the config is being read, so a typo fails startup (or the dynamic\n     * config update) instead of surfacing later as a per-alarm {@link URI#create} failure inside the hook callback,\n     * where every alarm would be dropped with only an error log.\n     *\n     * @param url      the configured endpoint\n     * @param hookName the hook the endpoint belongs to, for the error message\n     */\n    private void validateEventsApiUrl(String url, String hookName) {\n        final URI uri;\n        try {\n            uri = new URI(url);\n        } catch (URISyntaxException e) {\n            throw new IllegalArgumentException(\n                    \"PagerDuty hook: [\" + hookName + \"] events-api-url is malformed: [\" + url + \"].\", e);\n        }\n        final String scheme = uri.getScheme();\n        if (uri.getHost() == null\n                || !(\"https\".equalsIgnoreCase(scheme) || \"http\".equalsIgnoreCase(scheme))) {\n            throw new IllegalArgumentException(\n                    \"PagerDuty hook: [\" + hookName + \"] events-api-url must be an absolute http(s) URL, but was: [\"\n                            + url + \"].\");\n        }\n        // The integration key travels in the request body, so a non-TLS endpoint puts a credential on the wire.\n        if (!\"https\".equalsIgnoreCase(scheme)) {\n            log.warn(\n                    \"PagerDuty hook [{}] is configured with a non-https events-api-url [{}]. \"\n                            + \"The integration key is sent in the request body and will not be encrypted.\",\n                    hookName, url\n            );\n        }\n    }\n\n    /**\n     * Read PagerDuty hook config into {@link PagerDutySettings}\n     */\n    @SuppressWarnings(\"unchecked\")\n    private void readPagerDutyConfig(Map hooks, Rules rules) {","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-alarm-plugin/src/main/java/org/apache/skywalking/oap/server/core/alarm/provider/RulesReader.java#L389-L425","documentation":"IllegalArgumentException thrown by RulesReader.validateEventsApiUrl when the events-api-url parses as a URI but is not an absolute http(s) URL — either uri.getHost() is null (relative/authority-less URI) or the scheme is not http/https (e.g. ftp, mailto). PagerDuty notifications are sent as HTTP POSTs, so the client needs an absolute URL with a host.","triggerScenarios":"A PagerDuty hook URL like `events.pagerduty.com/v2/enqueue` (no scheme, so URI treats the whole thing as a path and host is null), `localhost:8080/x` without `http://` (parsed as scheme `localhost`), or a non-http scheme such as `ftp://...`. Only `http` and `https` (case-insensitive) with a non-null host pass.","commonSituations":"Omitting the scheme when editing config by hand; an env-var template that injects only the host part; using a service-short-name URL in Kubernetes (`http://pagerduty-proxy.svc` is fine but `pagerduty-proxy.svc` alone fails); switching from a fully-qualified URL to an internal short name and dropping `http://`.","solutions":["Prefix the URL with the scheme: `https://events.pagerduty.com/v2/enqueue`.","Note the follow-up warn: an http:// URL is accepted but the integration key travels unencrypted in the request body — prefer https, or use an http URL only against a TLS-terminating internal proxy.","If using templating, ensure the template renders scheme+host+path together, not just the host.","Restart OAP / re-push the config after fixing; validation is fail-fast at config read."],"exampleFix":"# before\nhooks:\n  pagerduty:\n    pagerduty-hook:\n      events-api-url: events.pagerduty.com/v2/enqueue  # no scheme, host==null\n# after\nhooks:\n  pagerduty:\n    pagerduty-hook:\n      events-api-url: https://events.pagerduty.com/v2/enqueue","handlingStrategy":"validation","validationCode":"// Accept only absolute http(s) with host, mirroring RulesReader:\nURI u = URI.create(url);\nboolean ok = u.getHost() != null\n    && (\"https\".equalsIgnoreCase(u.getScheme()) || \"http\".equalsIgnoreCase(u.getScheme()));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the scheme; prefer https because the PagerDuty integration key is sent in the body.","Add a CI lint that every configured webhook/PagerDuty URL matches ^https://[^/]+/.* .","Beware templated values that inject only a hostname."],"tags":["alarm","pagerduty","webhook","config"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}