{"record":{"id":"b8da34c29bdef0e0","repo":"jamiepine/voicebox","slug":"rewrite-needs-non-empty-text-to-restate","errorCode":null,"errorMessage":"Rewrite needs non-empty text to restate.","messagePattern":"Rewrite needs non-empty text to restate\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/personality.py","lineNumber":107,"sourceCode":"        prompt=\"Speak.\",\n        system=system_prompt,\n        max_tokens=256,\n        temperature=0.9,\n        model_size=resolved_size,\n    )\n    return PersonalityResult(text=output.strip(), model_size=resolved_size)\n\n\nasync def rewrite_as_profile(\n    personality: str | None,\n    user_text: str,\n    model_size: str | None = None,\n) -> PersonalityResult:\n    \"\"\"Restate the user's text in the character's voice, ideas intact.\"\"\"\n    character = _require_personality(personality)\n    cleaned = collapse_repetitive_artifacts(user_text)\n    if not cleaned.strip():\n        raise ValueError(\"Rewrite needs non-empty text to restate.\")\n\n    backend = llm_service.get_llm_model()\n    resolved_size = model_size or backend.model_size\n\n    system_prompt = _build_system_prompt(character, _REWRITE_TASK)\n    output = await backend.generate(\n        prompt=cleaned,\n        system=system_prompt,\n        max_tokens=1024,\n        temperature=0.3,\n        model_size=resolved_size,\n    )\n    return PersonalityResult(text=output.strip(), model_size=resolved_size)\n","sourceCodeStart":89,"sourceCodeEnd":121,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/personality.py#L89-L121","documentation":"Raised by rewrite_as_profile after collapse_repetitive_artifacts(user_text) produces an empty string (personality.py:105-107). The personality guard already passed; the input text was either empty, whitespace, or reduced to nothing by the artifact-collapser (e.g. input was only stutter/repetition that got collapsed away). This protects the LLM from a no-op prompt.","triggerScenarios":"POST /generate with personality=true and an empty text field; input was only whitespace or repeated characters ('....', 'aaaa'); input was only emoji or punctuation that collapse_repetitive_artifacts strips.","commonSituations":"Frontend allows submitting an empty textarea; paste glitch produced whitespace-only text; user typed only a character repetition that the collapser eats.","solutions":["Validate non-empty text client-side before enabling the submit button.","Pass the original (pre-collapse) text length to the guard, or trim whitespace before sending.","If using the API directly, ensure the text field has at least one non-whitespace, non-repetition character.","Adjust collapse_repetitive_artifacts if it is over-aggressively eating legitimate short inputs."],"exampleFix":"// before\nconst text = textarea.value; // may be whitespace\nawait api.post('/generate', {text, personality: true});\n// after\nconst text = textarea.value.trim();\nif (!text) return;\nawait api.post('/generate', {text, personality: true});","handlingStrategy":"validation","validationCode":"def rewrite_input_ok(user_text: str) -> bool:\n    return bool(user_text and user_text.strip())\n\n# before rewrite:\nif not rewrite_input_ok(user_text):\n    raise HTTPException(400, 'Text to rewrite must be non-empty.')","typeGuard":"def is_rewritable_text(s: object) -> bool:\n    return isinstance(s, str) and bool(s.strip())","tryCatchPattern":"try:\n    result = await rewrite_as_profile(personality, user_text)\nexcept ValueError as e:\n    if str(e) == 'Rewrite needs non-empty text to restate.':\n        raise HTTPException(400, str(e))\n    raise HTTPException(400, str(e))","preventionTips":["Trim and validate text length client-side before enabling submit.","Do not pass placeholder/repetition-only strings into rewrite.","If collapse_repetitive_artifacts over-strips, tune it rather than feeding it edge-case input."],"tags":["personality","llm","validation","rewrite","input"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}