{"record":{"id":"1c7a93cea12a0d33","repo":"jamiepine/voicebox","slug":"this-profile-has-no-personality-set-add-one-on-th","errorCode":null,"errorMessage":"This profile has no personality set. Add one on the profile to use compose or personality-rewrite.","messagePattern":"This profile has no personality set\\. Add one on the profile to use compose or personality-rewrite\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/services/personality.py","lineNumber":66,"sourceCode":"    \"\"\"What the three service functions return.\"\"\"\n\n    text: str\n    model_size: str\n\n\ndef _build_system_prompt(personality: str, task: str) -> str:\n    return (\n        _CHARACTER_FRAMING\n        + \"\\n\\nCharacter description:\\n\"\n        + personality.strip()\n        + \"\\n\\n\"\n        + task\n    )\n\n\ndef _require_personality(personality: str | None) -> str:\n    if not personality or not personality.strip():\n        raise ValueError(\n            \"This profile has no personality set. Add one on the profile to use compose or personality-rewrite.\"\n        )\n    return personality\n\n\nasync def compose_as_profile(\n    personality: str | None,\n    model_size: str | None = None,\n) -> PersonalityResult:\n    \"\"\"Produce a fresh utterance in the character's voice.\n\n    No user input; the system prompt plus a trigger user turn (\"Speak.\")\n    is all the model gets. Temperature is high so successive calls\n    produce different outputs — the UI's Compose button is expected to\n    be clicked repeatedly for variety.\n    \"\"\"\n    text = _require_personality(personality)\n    backend = llm_service.get_llm_model()","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/personality.py#L48-L84","documentation":"Raised by personality._require_personality when the profile's personality field is None or whitespace-only (personality.py:64-69). compose_as_profile and rewrite_as_profile both call this guard before touching the LLM, so the error fires before any model load. The personality prompt is what makes the character — without it, compose/rewrite is undefined behavior, so the service refuses.","triggerScenarios":"POST /profiles/{id}/compose on a profile whose personality column is null; POST /generate or /speak with personality=true against a cloned profile that never had a personality set; the personality field was cleared via an edit.","commonSituations":"Cloned-voice profile created before the personality feature existed; user edited the profile and blanked the personality field; UI exposes the Compose button on profiles that have no personality.","solutions":["Set a personality prompt on the profile: PATCH /profiles/{id} with a non-empty personality string.","If you hit this from the Compose button, hide/disable it in the UI when profile.personality is empty.","For rewrite flows, either set a personality or call generate without personality=true.","Backfill existing profiles with a default personality if the feature was added post-launch."],"exampleFix":"# before\nawait compose_as_profile(profile.personality)  # personality is None\n# after\nawait api.patch(f'/profiles/{pid}', {'personality': 'A gruff harbor master.'})\nawait compose_as_profile('A gruff harbor master.')","handlingStrategy":"type-guard","validationCode":"def has_personality(profile) -> bool:\n    return bool(getattr(profile, 'personality', None) and profile.personality.strip())\n\n# before composing/rewriting:\nif not has_personality(profile):\n    raise HTTPException(409, 'Set a personality on this profile before using compose/rewrite.')","typeGuard":"def can_compose(profile) -> bool:\n    p = getattr(profile, 'personality', None)\n    return isinstance(p, str) and bool(p.strip())","tryCatchPattern":"try:\n    result = await compose_as_profile(profile.personality)\nexcept ValueError as e:\n    if 'no personality set' in str(e):\n        raise HTTPException(409, str(e))\n    raise HTTPException(400, str(e))","preventionTips":["Disable the Compose button in the UI when profile.personality is empty.","Require personality at profile-create time for profiles intended for personality flows.","Backfill legacy cloned profiles with a default personality."],"tags":["personality","llm","validation","profile","precondition"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}