copier icon indicating copy to clipboard operation
copier copied to clipboard

"ValueError: Invalid Choice" with neither property name nor value

Open lhupfeldt opened this issue 9 months ago • 7 comments

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 ...

lhupfeldt avatar May 05 '25 15:05 lhupfeldt

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?

lhupfeldt avatar May 05 '25 16:05 lhupfeldt

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

yarikoptic avatar Nov 06 '25 17:11 yarikoptic

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']

yarikoptic avatar Nov 06 '25 18:11 yarikoptic

Can you share the choice question spec and answer?

sisp avatar Nov 06 '25 18:11 sisp

full content is at https://github.com/emberarchive/study-template/tree/broken-copier/.copier

yarikoptic avatar Nov 06 '25 19:11 yarikoptic

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.

sisp avatar Nov 06 '25 20:11 sisp

thank you @sisp, I will kick that claude code for doing it backwards! ;)

yarikoptic avatar Nov 06 '25 21:11 yarikoptic