{"record":{"id":"2148e12ac8d6af6e","repo":"jackwener/OpenCLI","slug":"mercury-returned-malformed-label-field-must","errorCode":null,"errorMessage":"Mercury returned malformed ${label}: ${field} must be boolean","messagePattern":"Mercury returned malformed (.+?): (.+?) must be boolean","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/mercury/utils.js","lineNumber":125,"sourceCode":"export function assertObject(value, label) {\n    if (!value || typeof value !== 'object' || Array.isArray(value)) {\n        throw new CommandExecutionError(`Mercury returned malformed ${label}`);\n    }\n    return value;\n}\n\nexport function assertMercuryState(value, label = 'page state') {\n    const state = assertObject(value, label);\n    if (typeof state.url !== 'string' || typeof state.title !== 'string') {\n        throw new CommandExecutionError(`Mercury returned malformed ${label}`);\n    }\n    return state;\n}\n\nfunction assertBooleanFields(state, label, fields) {\n    for (const field of fields) {\n        if (typeof state[field] !== 'boolean') {\n            throw new CommandExecutionError(`Mercury returned malformed ${label}: ${field} must be boolean`);\n        }\n    }\n    return state;\n}\n\nexport async function inspectMercury(page) {\n    await page.goto(MERCURY_EXPENSES_URL, { waitUntil: 'load', settleMs: 1500 });\n    await page.wait({ time: 1 });\n\n    const state = await page.evaluate(`(() => {\n        const text = document.body?.innerText || '';\n        const url = location.href;\n        return {\n            url,\n            loggedIn: !/\\\\/login\\\\b/.test(url) && !/sign in|log in|password|passkey/i.test(text),\n            hasSubmitExpense: /Submit expense/i.test(text),\n            hasReimbursements: /Reimbursements|My Expenses|Submitted Expenses/i.test(text),\n            title: document.title","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/mercury/utils.js#L107-L143","documentation":"assertBooleanFields walks a list of expected fields on a Mercury state object and requires each to be strictly boolean. Any field that is undefined, null, a string ('true'), or a number throws CommandExecutionError 'Mercury returned malformed <label>: <field> must be boolean'. It protects flag-style fields (e.g. logged-in/loading indicators) from ambiguous truthy values.","triggerScenarios":"Mercury state missing one of the required boolean fields; field serialized as string 'true'/'false' by an older/newer Mercury version; field present but null; payload passed through a transformation that coerced booleans to 0/1.","commonSituations":"Version drift between Mercury extension and CLI changing field types; JSON produced by a custom proxy/serializer that stringifies booleans; partially-failed page snapshots omitting fields; middleware logging layers rebuilding state objects.","solutions":["Identify the offending field from the error message and log the raw Mercury payload to confirm its type.","Update or downgrade the Mercury extension/CLI so both sides agree the field is a real boolean.","If values arrive as strings, coerce explicitly before validation: state[f] = state[f] === 'true'.","Ensure any proxy/middleware between Mercury and the CLI preserves JSON boolean types (no template-string rebuilding)."],"exampleFix":"// before\nconst state = JSON.parse(await redis.get('mercury-state')); // booleans stringified by writer\n// after\nconst state = JSON.parse(await redis.get('mercury-state'));\nfor (const f of ['loggedIn', 'ready']) if (typeof state[f] === 'string') state[f] = state[f] === 'true';\nassertBooleanFields(state, 'page state', ['loggedIn', 'ready']);","handlingStrategy":"type-guard","validationCode":"function hasBooleanFields(v, fields) {\n  return v !== null && typeof v === 'object' && fields.every(f => typeof v[f] === 'boolean');\n}","typeGuard":"function isMercuryStateWithFlags(v, fields) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n    && fields.every(f => typeof v[f] === 'boolean');\n}","tryCatchPattern":"try {\n  const state = await inspectMercury(page);\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('must be boolean')) {\n    const field = /: (\\w+) must be boolean/.exec(e.message)?.[1];\n    console.error(`Mercury flag field '${field}' has wrong type; check extension/CLI version match`);\n  } else throw e;\n}","preventionTips":["Don't rebuild Mercury JSON through string templating (coerces types)","Keep serializers type-preserving (no 'true' strings)","Pin matching Mercury extension and CLI versions","Sanity-check boolean flags after any intermediate storage (queues, caches)"],"tags":["runtime","integration","malformed-response","mercury","type-mismatch"],"backgroundTag":"malformed-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}