{"record":{"id":"12a97613b3e6263d","repo":"laurent22/joplin","slug":"errorprefix-field-output-is-not-an-object","errorCode":null,"errorMessage":"${errorPrefix}: Field \"output\" is not an object","messagePattern":"(.+?): Field \"output\" is not an object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/services/voiceTyping/whisper.ts","lineNumber":39,"sourceCode":"\t\tif (typeof json !== 'object') throw new Error('Whisper config is not an object');\n\n\t\tconst processPrompts = () => {\n\t\t\tif (!('prompts' in json)) return;\n\t\t\tif (typeof json.prompts !== 'object') {\n\t\t\t\tthrow new Error(`${errorPrefix}: Field \"prompts\" is not an object`);\n\t\t\t}\n\n\t\t\tfor (const [key, value] of Object.entries(json.prompts)) {\n\t\t\t\tif (typeof value !== 'string') {\n\t\t\t\t\tthrow new Error(`${errorPrefix}: Value for key ${key} is ${typeof value}, not string.`);\n\t\t\t\t}\n\t\t\t\tthis.prompts.set(key, value);\n\t\t\t}\n\t\t};\n\t\tconst processOutputSettings = () => {\n\t\t\tif (!('output' in json)) return;\n\t\t\tif (typeof json.output !== 'object') {\n\t\t\t\tthrow new Error(`${errorPrefix}: Field \"output\" is not an object`);\n\t\t\t}\n\n\t\t\tconst getReplacements = (key: string, value: unknown) => {\n\t\t\t\tif (!Array.isArray(value)) {\n\t\t\t\t\tthrow new Error(`${errorPrefix}: ${key} must be an array`);\n\t\t\t\t}\n\n\t\t\t\tconst results: [string, string][] = [];\n\t\t\t\tfor (const replacement of value) {\n\t\t\t\t\tif (!Array.isArray(replacement)) {\n\t\t\t\t\t\tthrow new Error(`${errorPrefix}: values for ${key} must be arrays`);\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof replacement[0] !== 'string' || typeof replacement[1] !== 'string') {\n\t\t\t\t\t\tthrow new Error(`${errorPrefix}: values for ${key} must be pairs of strings`);\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.push([replacement[0], replacement[1]]);\n\t\t\t\t}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/services/voiceTyping/whisper.ts#L21-L57","documentation":"Thrown by `WhisperConfig.processOutputSettings` when the `output` field is present but not an object. `output` is expected to be a container for `stringReplacements` and `regexReplacements`.","triggerScenarios":"Parsed config has `\"output\": \"...\"` or `\"output\": [...]` or any non-object value. Triggered after the top-level object check and once `processOutputSettings` runs.","commonSituations":"Hand-edited config; misnamed field; legacy format that put replacements at the top level instead of under `output`.","solutions":["Restructure config so `output` is an object: `{ \"output\": { \"stringReplacements\": [...] } }`.","Re-download the model bundle.","Schema-validate before shipping custom configs."],"exampleFix":"// before\n\"output\": [...]\n\n// after\n\"output\": { \"stringReplacements\": [] }","handlingStrategy":"validation","validationCode":"if ('output' in config && (typeof config.output !== 'object' || config.output === null || Array.isArray(config.output))) {\n  throw new Error('Whisper config: output must be an object');\n}","typeGuard":"const isOutputObject = (v: unknown): v is Record<string, unknown> =>\n  !!v && typeof v === 'object' && !Array.isArray(v);","tryCatchPattern":"null","preventionTips":["Nest stringReplacements / regexReplacements under output.","Schema-validate before loading.","Re-download the bundle on validation failure."],"tags":["voice-typing","whisper","config","validation"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}