{"record":{"id":"fd6c8f54dd5bea6a","repo":"louislam/uptime-kuma","slug":"keyword-monitor-keyword-is-keywordfound","errorCode":null,"errorMessage":"keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [\" + ${truncatedResponse} + \"]","messagePattern":"keyword \\[(.+?)\\] is (.+?) in \\[\" \\+ (.+?) \\+ \"\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/monitor-types/grpc.js","lineNumber":33,"sourceCode":"        const service = this.constructGrpcService(\n            monitor.grpcUrl,\n            monitor.grpcProtobuf,\n            monitor.grpcServiceName,\n            monitor.grpcEnableTls\n        );\n        let response = await this.grpcQuery(service, monitor.grpcMethod, monitor.grpcBody);\n        heartbeat.ping = dayjs().valueOf() - startTime;\n        log.debug(this.name, \"gRPC response:\", response);\n        let keywordFound = response.toString().includes(monitor.keyword);\n        if (keywordFound !== !monitor.isInvertKeyword()) {\n            log.debug(\n                this.name,\n                `GRPC response [${response}] + \", but keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [\" + ${response} + \"]\"`\n            );\n\n            let truncatedResponse = response.length > 50 ? response.toString().substring(0, 47) + \"...\" : response;\n\n            throw new Error(\n                `keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [\" + ${truncatedResponse} + \"]`\n            );\n        }\n        heartbeat.status = UP;\n        heartbeat.msg = `${response}, keyword [${monitor.keyword}] ${keywordFound ? \"is\" : \"not\"} found`;\n    }\n\n    /**\n     * Create gRPC client\n     * @param {string} url grpc Url\n     * @param {string} protobufData grpc ProtobufData\n     * @param {string} serviceName grpc ServiceName\n     * @param {string} enableTls grpc EnableTls\n     * @returns {grpc.Service} grpc Service\n     */\n    constructGrpcService(url, protobufData, serviceName, enableTls) {\n        const protocObject = protojs.parse(protobufData);\n        const protoServiceObject = protocObject.root.lookupService(serviceName);","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/monitor-types/grpc.js#L15-L51","documentation":"Thrown by the gRPC keyword monitor when the keyword presence in the response does not match isInvertKeyword(). NOTE: there is a formatting bug in the thrown message — the template literal mixes backtick interpolation with literal string-concatenation syntax, so the output reads `...in [\" + <value> + \"]` with literal quotes and plus signs rather than clean text. The underlying logic (keyword mismatch) is unaffected.","triggerScenarios":"keywordFound !== !monitor.isInvertKeyword(). If not inverted and keyword absent -> throw 'not present'; if inverted and keyword present -> throw 'present'. The response is stringified then truncated to 47 chars for the message.","commonSituations":"Wrong keyword for the gRPC response payload, the service moved to a different message shape, invert keyword toggled by mistake, or the response is an error message that does/doesn't contain the keyword.","solutions":["Confirm the keyword actually appears in the raw gRPC response string","Verify isInvertKeyword: invert means UP when the keyword is ABSENT","Serialize the real gRPC response once to confirm what string is being searched","Optionally fix the cosmetic template-literal bug so the message is readable"],"exampleFix":"// before (line 33-35): broken template/concat mix\nthrow new Error(\n    `keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [\" + ${truncatedResponse} + \"]`\n);\n// after: clean template literal\nthrow new Error(\n    `keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [${truncatedResponse}]`\n);","handlingStrategy":"validation","validationCode":"// Validate gRPC keyword config before running\nif (!monitor.keyword || monitor.keyword.length === 0) {\n    throw new Error(\"gRPC keyword monitor has no keyword set\");\n}","typeGuard":null,"tryCatchPattern":"// Preserve the keyword result classification for alerting\nif (keywordFound !== !monitor.isInvertKeyword()) {\n    throw new Error(`keyword [${monitor.keyword}] is ${keywordFound ? \"present\" : \"not\"} in [${truncatedResponse}]`);\n}","preventionTips":["Confirm the keyword appears in the stringified gRPC response","Remember isInvertKeyword semantics: invert = UP when keyword is absent","Fix the cosmetic template-literal bug so alert messages are readable"],"tags":["grpc","keyword","config","message-bug"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}