ToolJet/ToolJet · error · QueryError
Invalid JSON message
Error message
Invalid JSON message
What it means
QueryError thrown by the legacy grpc plugin when queryOptions.jsonMessage is present but JSON.parse fails. The rpc call needs a JSON object as its request payload, so a syntactically invalid string is rejected before invoking the method. The error carries empty objects for data and metadata, so the original parse message is lost.
Source
Thrown at plugins/packages/grpc/lib/index.ts:54
if (authType === 'basic') {
metadata.add('username', sourceOptions.username);
metadata.add('password', sourceOptions.password);
}
if (authType === 'bearer') {
metadata.add('Authorization', `Bearer ${sourceOptions.bearer_token}`);
}
if (authType === 'api_key') {
metadata.add(sourceOptions.grpc_apikey_key, sourceOptions.grpc_apikey_value);
}
let jsonMessage = {};
if (queryOptions.jsonMessage) {
try {
jsonMessage = JSON.parse(queryOptions.jsonMessage);
} catch (e) {
throw new QueryError('Invalid JSON message', {}, {});
}
}
const result = await new Promise((resolve, reject) => {
clientStub[rpc](jsonMessage, metadata, (err: any, response: any) => {
if (err) {
reject(err);
}
resolve(response);
});
}).catch((err) => {
throw new QueryError(err.message, {}, {});
});
return {
status: 'ok',
data: result as any,
};View on GitHub (pinned to 20602a8e10)
Solutions
- Validate the message parses as JSON before calling run (JSON.parse in a try/catch).
- Use strict JSON: double quotes, no trailing commas, no comments, no undefined.
- If the payload contains variables, render then re-validate before sending.
Example fix
// before
run(srcOpts, { serviceName, rpc, jsonMessage: "{name: 'foo',}" });
// after
run(srcOpts, { serviceName, rpc, jsonMessage: '{"name":"foo"}' }); Defensive patterns
Strategy: validation
Validate before calling
function parseJsonMessage(raw: string): Record<string, unknown> {
try {
return JSON.parse(raw);
} catch {
throw new Error('jsonMessage is not valid JSON');
}
}
if (queryOptions.jsonMessage) parseJsonMessage(queryOptions.jsonMessage);
await grpcPlugin.run(sourceOptions, queryOptions, dataSourceId); Type guard
const isJsonString = (s: string): boolean => {
try { JSON.parse(s); return true; } catch { return false; }
}; Try / catch
try {
await grpcPlugin.run(sourceOptions, queryOptions, dataSourceId);
} catch (e) {
if (e instanceof QueryError && e.message === 'Invalid JSON message') {
surfaceJsonError(queryOptions.jsonMessage);
}
throw e;
} Prevention
- Lint the request message as JSON in the editor.
- Render templated variables then re-validate before send.
- Avoid JS-only syntax (single quotes, comments, undefined).
When it happens
Trigger: User enters a request message with a trailing comma, single quotes, or unquoted keys; copy/paste of a JS object literal instead of JSON; an empty-but-non-empty string of whitespace past the truthiness check.
Common situations: Query editor with hand-typed JSON; payload templated with a variable that rendered to invalid JSON; mixing JS syntax (undefined, comments) into the message.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of ToolJet/ToolJet@20602a8e10 (2026-08-13).
Data as JSON: /api/errors/41bee9cb55b3ce39.
Report an issue: GitHub.