"ValueError: Invalid Choice" with neither property name nor value
Describe the problem
During copier update I get ValueError: Invalid Choice but there is no mention of the property name or the invalid value.
This is caused an earlier update in copier.yaml making difficult to debug (i.e. I don't know which change is causing this.)
Issue may be related to multi select choice, I will update if I figure it out.
Template
Sorry, cannot provide the template.
To Reproduce
No response
Logs
Expected behavior
The exception message includes property name, valid choices and bad choice causing the error.
Screenshots/screencasts/logs
No response
Operating system
Linux
Operating system distribution and version
Fedora 40
Copier version
9.7.1
Python version
3.12.9
Installation method
uvx+pypi
Additional context
Install is uv venv ...; uv pip ...
The issue was caused by a wrong handling of a merge conflict storing a value for a multi select property which was invalid for the template version registered in copier-answers. But is it really necessary to throw an exception in this case? Why not simply prompt for the new value or accept valid data argument?
I have ran into this as well! I am a newb to copier and exploring it via AI so yet to grasp what leads to this but I concur that ideally copier should provide details on this crash instead of just a long traceback with the message which does not give detail on what lead to it .
full protocol to reproduce
❯ git clone -b broken-copier http://github.com/emberarchive/study-template
Cloning into 'study-template'...
warning: redirecting to https://github.com/emberarchive/study-template/
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 10 (delta 0), reused 7 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (10/10), 6.34 KiB | 6.34 MiB/s, done.
❯ cd study-template
LICENSE
❯ copier copy .copier .
🎤 Name of your BIDS dataset
my-dataset
🎤 Brief description of your dataset
(Finish with 'Alt+Enter' or 'Esc then Enter')
> BIDS dataset
Traceback (most recent call last):
File "/home/yoh/proj/ember/study-template/.venv/bin/copier", line 10, in <module>
sys.exit(CopierApp.run())
^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/plumbum/cli/application.py", line 631, in run
inst, retcode = subapp.run(argv, exit=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/plumbum/cli/application.py", line 626, in run
retcode = inst.main(*tailargs)
^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_cli.py", line 284, in main
return _handle_exceptions(inner)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_cli.py", line 71, in _handle_exceptions
method()
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_cli.py", line 275, in inner
with self._worker(
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_main.py", line 272, in __exit__
raise value
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_cli.py", line 282, in inner
worker.run_copy()
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_main.py", line 96, in _wrapper
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_main.py", line 1038, in run_copy
self._ask()
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_main.py", line 600, in _ask
[question.get_questionary_structure()],
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_user_data.py", line 398, in get_questionary_structure
default = self.get_default_rendered()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_user_data.py", line 297, in get_default_rendered
default = self.get_default()
^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_user_data.py", line 279, in get_default
result = self.parse_answer(result)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_user_data.py", line 502, in parse_answer
return self._parse_answer(answer)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/yoh/proj/ember/study-template/.venv/lib/python3.11/site-packages/copier/_user_data.py", line 517, in _parse_answer
raise ValueError(
ValueError: Invalid choice
is my template is wrong or we should just match to the title in addition to value?
traceback and pdb exploration
504 def _parse_answer(self, answer: Any) -> Any:
505 """Parse a single answer according to the question's type."""
(Pdb)
506 ans = self.cast_answer(answer)
507 choices = self._formatted_choices
508 if not choices:
509 return ans
510 choice_error = ""
511 for choice in choices:
512 if ans == self.cast_answer(choice.value):
513 if not choice.disabled:
514 return ans
515 if not choice_error:
516 choice_error = choice.disabled
(Pdb)
517 -> raise ValueError(
518 f"Invalid choice: {choice_error}" if choice_error else "Invalid choice"
519 )
520
521
522 def parse_yaml_string(string: str) -> Any:
523 """Parse a YAML string and raise a ValueError if parsing failed.
524
525 This method is needed because :meth:`prompt` requires a ``ValueError``
526 to repeat failed questions.
527 """
(Pdb) p choices
[<questionary.prompts.common.Choice object at 0x7f61adc81a10>, <questionary.prompts.common.Choice object at 0x7f61adcc1910>, <questionary.prompts.common.Choice object at 0x7f61adea9910>]
(Pdb) p dir(choices[0])
['_Choice__auto_shortcut', '_Choice__shortcut_key', '__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'auto_shortcut', 'build', 'checked', 'description', 'disabled', 'get_shortcut_title', 'shortcut_key', 'title', 'value']
(Pdb) p choices[0]
<questionary.prompts.common.Choice object at 0x7f61adc81a10>
(Pdb) p choices[0].title
'raw'
(Pdb) p ans
'study'
(Pdb) p [x.title for x in choices]
['raw', 'derivative', 'study']
(Pdb) p answer
'study'
(Pdb) p [x.value for x in choices]
['Raw neuroimaging data', 'Processed/analyzed data', 'Complete study with raw and derivatives']
Can you share the choice question spec and answer?
full content is at https://github.com/emberarchive/study-template/tree/broken-copier/.copier
The choices mapping works the other way around (<title>: <value>):
choices:
- raw: Raw neuroimaging data
- derivative: Processed/analyzed data
- study: Complete study with raw and derivatives
+ Raw neuroimaging data: raw
+ Processed/analyzed data: derivative
+ Complete study with raw and derivatives: study
But the error message and stacktrace are definitely suboptimal. I'll try to find some time to improve this.
thank you @sisp, I will kick that claude code for doing it backwards! ;)