abhigyanpatwari/GitNexus · error
"params" must be a plain object with scalar values (string/n
Error message
"params" must be a plain object with scalar values (string/number/boolean/null)
What it means
HTTP 400 from POST /api/query when the optional params field is present but fails isValidQueryParams: it must be a plain object whose values are scalars only (string, number, boolean, or null). Because the cypher is executed via executePrepared against LadybugDB, allowing arrays, nested objects, or other types would either break prepared-statement binding or smuggle structure into the query layer, so the API validates before opening the read-only connection.
Source
Thrown at gitnexus/src/server/api.ts:656
res.status(statusFromError(err)).json({ error: err.message || 'Failed to read file' });
}
}
};
export const handleQueryRequest = async (
req: express.Request,
res: express.Response,
resolveRepo: (repoName?: string) => Promise<{ storagePath: string } | undefined>,
): Promise<void> => {
try {
const cypher = req.body.cypher as string;
if (!cypher) {
res.status(400).json({ error: 'Missing "cypher" in request body' });
return;
}
const queryParams = req.body.params;
if (queryParams !== undefined && !isValidQueryParams(queryParams)) {
res.status(400).json({
error: '"params" must be a plain object with scalar values (string/number/boolean/null)',
});
return;
}
const entry = await resolveRepo(requestedRepo(req));
if (!entry) {
res.status(404).json({ error: 'Repository not found' });
return;
}
const lbugPath = path.join(entry.storagePath, 'lbug');
const result = await withLbugDb(lbugPath, () => executePrepared(cypher, queryParams ?? {}), {
readOnly: true,
});
res.json({ result });
} catch (err: any) {
if (isReadOnlyDbError(err)) {
res.status(403).json({ error: 'Write queries are not allowed via the HTTP API' });View on GitHub (pinned to aac7515d2a)
Solutions
- Flatten params to scalars: {"params": {"name": "foo", "limit": 10, "flag": true}} — one level, scalar values only
- Omit params entirely when the query has none
- Stringify or JSON-encode nested structures before sending if the query needs them, and decode in Cypher
- Validate client-side with a type guard mirroring the server's rule before POSTing
Example fix
// before
body: JSON.stringify({ cypher, params: { filters: { name: 'x' } } }) // nested → 400
// after
body: JSON.stringify({ cypher, params: { name: 'x' } }) // scalar values only Defensive patterns
Strategy: type-guard
Validate before calling
// Flatten params to scalar values before sending.
function flattenParams(obj: Record<string, unknown>): Record<string, string | number | boolean | null> {
const out: Record<string, string | number | boolean | null> = {};
for (const [k, v] of Object.entries(obj)) {
out[k] = (v !== null && typeof v === 'object') ? JSON.stringify(v) : (v as string | number | boolean | null);
}
return out;
} Type guard
type Scalar = string | number | boolean | null;
function isValidQueryParams(v: unknown): v is Record<string, Scalar> {
if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;
return Object.values(v).every((x) => x === null || ['string', 'number', 'boolean'].includes(typeof x));
} Prevention
- Send params as one flat object of scalars only
- Stringify nested structures yourself if a query needs them
- Omit params entirely when the query has none
When it happens
Trigger: Sending params as an array [1,2,3]; nesting objects {filter:{name:'x'}}; passing a serialized string 'name=x'; sending true/false boxed weirdly via JSON revival; passing a params value that is an array of one element expecting it to bind as a scalar.
Common situations: Porting a query client from a Neo4j driver that accepts array params; frontend state objects being passed wholesale as params; Date objects (they serialize to strings via JSON, but hand-built clients may pass them as objects); users trying to sneak full filter objects through the HTTP API.
Related errors
- Missing path
- Missing "cypher" in request body
- "url" must be a string
- "path" must be a string
- Provide "url" (git URL) or "path" (local path)
AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20).
Data as JSON: /api/errors/68dd2f10c38a1503.
Report an issue: GitHub.