{"record":{"id":"06fe931edf61128f","repo":"laurent22/joplin","slug":"errorprefix-value-for-key-key-is-typeof-v","errorCode":null,"errorMessage":"${errorPrefix}: Value for key ${key} is ${typeof value}, not string.","messagePattern":"(.+?): Value for key (.+?) is (.+?), not string\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/services/voiceTyping/whisper.ts","lineNumber":31,"sourceCode":"class WhisperConfig {\n\tpublic prompts: Map<string, string> = new Map();\n\tpublic supportsShortAudioCtx = false;\n\tpublic stringReplacements: [string, string][] = [];\n\tpublic regexReplacements: [RegExp, string][] = [];\n\n\tpublic constructor(json: unknown) {\n\t\tconst errorPrefix = 'Whisper config';\n\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)) {","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/services/voiceTyping/whisper.ts#L13-L49","documentation":"Thrown while iterating `Object.entries(json.prompts)` in `WhisperConfig.processPrompts` when one of the prompt values is not a string. Each prompt must map a locale key to a string.","triggerScenarios":"A config like `{ \"prompts\": { \"en\": 123 } }` or `{ \"prompts\": { \"en\": { ... } } }` — any value that fails `typeof value !== 'string'`. The offending key and the actual `typeof` are interpolated.","commonSituations":"Authoring error where a prompt value is a number or nested object; copy-paste mistake; locale key without a string value.","solutions":["Ensure every value under `prompts` is a string literal.","Re-download the model bundle for a known-good config.","Add a JSON schema check that `prompts` is `Record<string,string>`."],"exampleFix":"// before\n\"prompts\": { \"en\": 123 }\n\n// after\n\"prompts\": { \"en\": \"transcribe audio\" }","handlingStrategy":"validation","validationCode":"for (const [k, v] of Object.entries(config.prompts ?? {})) {\n  if (typeof v !== 'string') {\n    throw new Error(`Whisper config: prompt value for ${k} must be a string`);\n  }\n}","typeGuard":"const isStringRecord = (v: unknown): v is Record<string, string> =>\n  !!v && typeof v === 'object' && !Array.isArray(v) &&\n  Object.values(v).every(x => typeof x === 'string');","tryCatchPattern":"null","preventionTips":["Keep all prompt values as plain strings.","Schema-validate prompts as Record<string,string>.","Re-download the model 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"}