{"record":{"id":"5f9bef20ce28a98e","repo":"HeyPuter/puter","slug":"field-required","errorCode":"field_required","errorMessage":"Missing required field: text","messagePattern":"Missing required field: text","errorType":"validation","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/drivers/ai-tts/providers/awsPolly/AWSPollyTTSProvider.ts","lineNumber":248,"sourceCode":"        } = args;\n\n        if (test_mode) {\n            return { url: SAMPLE_AUDIO_URL, content_type: 'audio' };\n        }\n\n        if (!VALID_ENGINES.includes(engine)) {\n            throw new HttpError(\n                400,\n                `Invalid engine: ${engine}. Valid engines: ${VALID_ENGINES.join(', ')}`,\n                {\n                    legacyCode: 'invalid_engine',\n                    fields: { engine, valid_engines: VALID_ENGINES },\n                },\n            );\n        }\n\n        if (typeof text !== 'string' || text.trim() === '') {\n            throw new HttpError(400, 'Missing required field: text', {\n                legacyCode: 'field_required',\n                fields: { key: 'text' },\n            });\n        }\n\n        const actor = Context.get('actor')!;\n        const usageType = `aws-polly:${engine}:character`;\n        const ucentsPerChar = AWS_POLLY_COSTS[engine] ?? 0;\n        const totalCost = ucentsPerChar * text.length;\n\n        const usageAllowed = await this.meteringService.hasEnoughCredits(\n            actor,\n            totalCost,\n        );\n        if (!usageAllowed) {\n            throw new HttpError(402, 'Insufficient funds', {\n                legacyCode: 'insufficient_funds',\n            });","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-tts/providers/awsPolly/AWSPollyTTSProvider.ts#L230-L266","documentation":"AWSPollyTTSProvider.synthesize requires a non-empty text string. After the engine check, if text is not a string or trims to empty, it throws HTTP 400 (legacyCode field_required) with fields.key='text'. This runs before voice resolution and credit checks.","triggerScenarios":"Calling synthesize with text omitted, null, a number, an empty string, or only whitespace; passing SSML in a separate field while leaving text blank.","commonSituations":"UI submitting before the user typed anything; passing the prompt into the wrong argument; whitespace-only input from a trimmed textarea; test calls that forget the text field.","solutions":["Pass a non-empty text string: { text: 'Hello world', provider: 'aws-polly' }.","Validate/trim text on the client before submitting; reject empty input upstream.","If sending SSML, put it in the text field with ssml: true (the provider routes on that flag)."],"exampleFix":"// before\nawait driver.synthesize({ provider: 'aws-polly', engine: 'neural' }); // no text\n// after\nawait driver.synthesize({ text: 'Hello world', provider: 'aws-polly', engine: 'neural' });","handlingStrategy":"validation","validationCode":"function synthesizePolly(text, opts = {}) {\n  if (typeof text !== 'string' || text.trim() === '') {\n    throw new Error('text is required and must be a non-empty string');\n  }\n  return driver.synthesize({ text, provider: 'aws-polly', ...opts });\n}","typeGuard":"const isNonEmptyString = (v: unknown): v is string =>\n  typeof v === 'string' && v.trim().length > 0;\n\nif (isNonEmptyString(text)) {\n  await driver.synthesize({ text, provider: 'aws-polly', engine: 'neural' });\n}","tryCatchPattern":null,"preventionTips":["Trim and validate text on the client before submission.","Disable the submit button while the text field is empty/whitespace.","When sending SSML, still pass it in text with ssml: true."],"tags":["aws-polly","tts","validation","required-field","input"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}