Skip to content

Add --dataloader_multiprocessing_context to work around Python 3.14 incompatibility - #9941

Open
sliedes wants to merge 1 commit into
modelscope:mainfrom
sliedes:data-loader-ctx
Open

Add --dataloader_multiprocessing_context to work around Python 3.14 incompatibility#9941
sliedes wants to merge 1 commit into
modelscope:mainfrom
sliedes:data-loader-ctx

Conversation

@sliedes

@sliedes sliedes commented Aug 18, 2026

Copy link
Copy Markdown

PR type

  • Bug Fix
  • New Feature
  • Document Updates
  • More Models or Datasets Support

PR information

Background

In Python 3.14, the default multiprocessing method on Linux changed from fork to the much safer forkserver. This, however, exposes a latent issue in swift (see #4586): The data loader has a reference to the entire model, which Python tries to pickle and send to the workers.

It seems someone has previously taken care to not serialize the entire model:

template.model = None # Avoid serializing the model.

This protects one serialization path, but after restoring template.model, __post_process_datasets(), wraps the dataset using template.encode as a bound method which makes the model reachable again:

dataset = LazyLLMDataset(dataset, template.encode, strict=args.strict, random_state=args.data_seed)

This was bad already before Python 3.14; CUDA and fork do not go well together. However, it has worked in practice because the data loader doesn't touch anything CUDA.

In practice, when using Python 3.14, I have used --dataloader_num_workers 0 to work around this problem; however, that's obviously not ideal.

What would a proper fix look like

I think a proper fix would ensure that the model is not reachable from the DataLoader so that it can be properly pickled. Perhaps distinguishing a worker template from a model-bound training template; conceptually

training template
    processor
    template logic
    model ──────────────> live CUDA model

worker template
    processor
    template logic
    model = None

The DataLoader's dataset and collator get the worker-safe one. Model-dependent _post_encode happens in the main training process with the real one.

What this PR does

I am hesitant to do bigger refactorings of a code base I do not understand well enough, so this PR provides what I consider a minimal reasonable workaround for the current state of things: It allows specifying an optional parameter --dataloader_multiprocessing_context, with valid options fork, forkserver, spawn, defaulting to None, which I believe is the reasonable safe choice as this really only papers over a bug. Now, with --dataloader_multiprocessing_context fork my training runs work on Python 3.14.

There's a wart: Transformers do not, in released versions, expose a parameter to do this; hence, the PR overrides _get_dataloader and changes the loader's held context. In main, transformers has adopted this parameter:

https://github.com/huggingface/transformers/blob/f9b76f2dfc44fdc6109468925bf0e1856fd34278/src/transformers/training_args.py#L1318

Reflections

I'm sending this hope it's useful. I would be more than happy if someone actually fixed the underlying issue of the data loader having a reference to the model (and thus being unpickleable) instead. I hope this at least demonstrates what the issue is even if deemed too hacky to merge.

@tastelikefeet

Copy link
Copy Markdown
Collaborator

Thanks for the contribution! Based on your code, we've attempted a fix for both the template pickling and CUDA initialization issues — could you check whether this works for your use case: https://github.com/modelscope/ms-swift/pull/9979/changes

@sliedes

sliedes commented Aug 28, 2026

Copy link
Copy Markdown
Author

Thanks! It seems that has already been merged in main? I tested current main (087aa1c) and still see this. Running:

swiftwrap/.venv/bin/swift sft \
    --external_plugins swiftwrap/src/swiftwrap/jxl_plugin.py \
    --model Qwen/Qwen3.5-0.8B-Base \
    --use_hf \
    --tuner_type lora \
    --dataset train.jsonl \
    --val_dataset val.jsonl \
    --split_dataset_ratio 0 \
    --add_non_thinking_prefix true \
    --loss_scale ignore_empty_think \
    --torch_dtype bfloat16 \
    --target_modules all-linear \
    --max_pixels $((2880 * 2880)) \
    --freeze_llm false \
    --freeze_vit false \
    --freeze_aligner true \
    --lora_rank 4 \
    --lora_alpha 32 \
    --learning_rate 1e-4 \
    --vit_lr 1e-5 \
    --lora_dropout 0.8 \
    --max_grad_norm 5.0 \
    --per_device_train_batch_size 1 \
    --gradient_accumulation_steps 3 \
    --num_train_epochs 20 \
    --warmup_steps 5 \
    --dataloader_num_workers 2 \
    --eval_on_start true \
    --eval_strategy steps \
    --eval_steps 5 \
    --save_strategy steps \
    --save_steps 5 \
    --early_stop_interval 8 \
    --save_total_limit 10 \
    --logging_steps 1 \
    --save_only_model true \
    --output_dir $OUTPUT

Output:

[INFO:swift] model_parameter_info: PeftModelForCausalLM: 856.2814M Params (3.2955M Trainable [0.3849%]), 0.0001M Buffers.
[INFO:swift] use_reentrant: True
[INFO:swift] The logging file will be saved in: /home/sliedes/proj/archives/ocr_finetune/output/0.8b/qwen3.5-0.8b-base-fi-lora/v18-20260828-034429/logging.jsonl
[INFO:swift] Successfully registered post_encode hook: ['PeftModelForCausalLM'].
[transformers] The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'eos_token_id': 248044, 'pad_token_id': 248044}.
Download complete: :                                                                          |  0.00B
Reconstruction complete: |                                                           |  0.00B /  0.00B
Download complete: :                                                                          |  0.00B
Reconstruction complete: |                                                           |  0.00B /  0.00B
[INFO:swift] vit_lr: 1e-05, aligner_lr: 0.0001, llm_lr: 0.0001
Train:   0%|                                                                              | 0/760 [00:00<?, ?it/s]/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py:438: UserWarning: Got pickle error when attempting to start a worker Process. This might be because the worker Process arguments are not picklable. Python 3.14+ changed the multiprocessing start method in non-Mac POSIX platforms to 'forkserver', which requires the worker Process arguments to be picklable. You can also try multiprocessing.set_start_method('fork').
  return _MultiProcessingDataLoaderIter(self)
[INFO:swift] last_model_checkpoint: None
[INFO:swift] best_model_checkpoint: None
[INFO:swift] images_dir: /home/sliedes/proj/archives/ocr_finetune/output/0.8b/qwen3.5-0.8b-base-fi-lora/v18-20260828-034429/images
Traceback (most recent call last):
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/cli/sft.py", line 20, in <module>
    sft_main()
    ~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 345, in sft_main
    return SwiftSft(args).main()
           ~~~~~~~~~~~~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/base.py", line 52, in main
    result = self.run()
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/ray_utils/base.py", line 168, in wrapper
    return func(self, *args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 189, in run
    return self.train(trainer)
           ~~~~~~~~~~^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 263, in train
    trainer.train(resume_checkpoint)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/trainers/mixin.py", line 1019, in train
    res = super().train(*args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 1433, in train
    return inner_training_loop(
        args=args,
    ...<2 lines>...
        ignore_keys_for_eval=ignore_keys_for_eval,
    )
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 1511, in _inner_training_loop
    self._evaluate(trial, ignore_keys_for_eval, skip_scheduler=True)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 3016, in _evaluate
    metrics = self.evaluate(ignore_keys=ignore_keys_for_eval)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/trainers/seq2seq_trainer.py", line 61, in evaluate
    res = super().evaluate(*args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer_seq2seq.py", line 193, in evaluate
    return super().evaluate(eval_dataset, ignore_keys=ignore_keys, metric_key_prefix=metric_key_prefix)
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 2603, in evaluate
    output = self.evaluation_loop(
        eval_dataloader,
    ...<5 lines>...
        metric_key_prefix=metric_key_prefix,
    )
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 2718, in evaluation_loop
    for step, inputs in enumerate(dataloader):
                        ~~~~~~~~~^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/accelerate/data_loader.py", line 582, in __iter__
    dataloader_iter = self.base_dataloader.__iter__()
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 505, in __iter__
    return self._get_iterator()
           ~~~~~~~~~~~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 438, in _get_iterator
    return _MultiProcessingDataLoaderIter(self)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 1176, in __init__
    w.start()
    ~~~~~~~^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
                  ~~~~~~~~~~~^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/context.py", line 300, in _Popen
    return Popen(process_obj)
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_forkserver.py", line 35, in __init__
    super().__init__(process_obj)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_fork.py", line 20, in __init__
    self._launch(process_obj)
    ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_forkserver.py", line 47, in _launch
    reduction.dump(process_obj, buf)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
_pickle.PicklingError: Can't pickle local object <function PreTrainedModel.enable_input_require_grads.<locals>.make_inputs_require_grads at 0x7d8bfce966c0>
when serializing collections.OrderedDict item 2
when serializing collections.OrderedDict object
when serializing dict item '_forward_hooks'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionPatchEmbed state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionPatchEmbed object
when serializing dict item 'patch_embed'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionModel state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionModel object
when serializing dict item 'visual'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5Model state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5Model object
when serializing dict item 'model'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration object
when serializing dict item 'model'
when serializing dict item '_modules'
when serializing peft.tuners.lora.model.LoraModel state
when serializing peft.tuners.lora.model.LoraModel object
when serializing dict item 'base_model'
when serializing dict item '_modules'
when serializing peft.peft_model.PeftModelForCausalLM state
when serializing peft.peft_model.PeftModelForCausalLM object
when serializing tuple item 0
when serializing list item 0
when serializing dict item '_handles'
when serializing swift.template.templates.qwen.Qwen3_5Template state
when serializing swift.template.templates.qwen.Qwen3_5Template object
when serializing tuple item 0
when serializing method reconstructor arguments
when serializing method object
when serializing dict item 'encode_func'
when serializing swift.dataset.utils.LazyLLMDataset state
when serializing swift.dataset.utils.LazyLLMDataset object
when serializing tuple item 1
when serializing dict item '_args'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object

@tastelikefeet

Copy link
Copy Markdown
Collaborator

Thanks! It seems that has already been merged in main? I tested current main (087aa1c) and still see this. Running:

swiftwrap/.venv/bin/swift sft \
    --external_plugins swiftwrap/src/swiftwrap/jxl_plugin.py \
    --model Qwen/Qwen3.5-0.8B-Base \
    --use_hf \
    --tuner_type lora \
    --dataset train.jsonl \
    --val_dataset val.jsonl \
    --split_dataset_ratio 0 \
    --add_non_thinking_prefix true \
    --loss_scale ignore_empty_think \
    --torch_dtype bfloat16 \
    --target_modules all-linear \
    --max_pixels $((2880 * 2880)) \
    --freeze_llm false \
    --freeze_vit false \
    --freeze_aligner true \
    --lora_rank 4 \
    --lora_alpha 32 \
    --learning_rate 1e-4 \
    --vit_lr 1e-5 \
    --lora_dropout 0.8 \
    --max_grad_norm 5.0 \
    --per_device_train_batch_size 1 \
    --gradient_accumulation_steps 3 \
    --num_train_epochs 20 \
    --warmup_steps 5 \
    --dataloader_num_workers 2 \
    --eval_on_start true \
    --eval_strategy steps \
    --eval_steps 5 \
    --save_strategy steps \
    --save_steps 5 \
    --early_stop_interval 8 \
    --save_total_limit 10 \
    --logging_steps 1 \
    --save_only_model true \
    --output_dir $OUTPUT

Output:

[INFO:swift] model_parameter_info: PeftModelForCausalLM: 856.2814M Params (3.2955M Trainable [0.3849%]), 0.0001M Buffers.
[INFO:swift] use_reentrant: True
[INFO:swift] The logging file will be saved in: /home/sliedes/proj/archives/ocr_finetune/output/0.8b/qwen3.5-0.8b-base-fi-lora/v18-20260828-034429/logging.jsonl
[INFO:swift] Successfully registered post_encode hook: ['PeftModelForCausalLM'].
[transformers] The tokenizer has new PAD/BOS/EOS tokens that differ from the model config and generation config. The model config and generation config were aligned accordingly, being updated with the tokenizer's values. Updated tokens: {'eos_token_id': 248044, 'pad_token_id': 248044}.
Download complete: :                                                                          |  0.00B
Reconstruction complete: |                                                           |  0.00B /  0.00B
Download complete: :                                                                          |  0.00B
Reconstruction complete: |                                                           |  0.00B /  0.00B
[INFO:swift] vit_lr: 1e-05, aligner_lr: 0.0001, llm_lr: 0.0001
Train:   0%|                                                                              | 0/760 [00:00<?, ?it/s]/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py:438: UserWarning: Got pickle error when attempting to start a worker Process. This might be because the worker Process arguments are not picklable. Python 3.14+ changed the multiprocessing start method in non-Mac POSIX platforms to 'forkserver', which requires the worker Process arguments to be picklable. You can also try multiprocessing.set_start_method('fork').
  return _MultiProcessingDataLoaderIter(self)
[INFO:swift] last_model_checkpoint: None
[INFO:swift] best_model_checkpoint: None
[INFO:swift] images_dir: /home/sliedes/proj/archives/ocr_finetune/output/0.8b/qwen3.5-0.8b-base-fi-lora/v18-20260828-034429/images
Traceback (most recent call last):
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/cli/sft.py", line 20, in <module>
    sft_main()
    ~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 345, in sft_main
    return SwiftSft(args).main()
           ~~~~~~~~~~~~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/base.py", line 52, in main
    result = self.run()
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/ray_utils/base.py", line 168, in wrapper
    return func(self, *args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 189, in run
    return self.train(trainer)
           ~~~~~~~~~~^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/pipelines/train/sft.py", line 263, in train
    trainer.train(resume_checkpoint)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/trainers/mixin.py", line 1019, in train
    res = super().train(*args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 1433, in train
    return inner_training_loop(
        args=args,
    ...<2 lines>...
        ignore_keys_for_eval=ignore_keys_for_eval,
    )
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 1511, in _inner_training_loop
    self._evaluate(trial, ignore_keys_for_eval, skip_scheduler=True)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 3016, in _evaluate
    metrics = self.evaluate(ignore_keys=ignore_keys_for_eval)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/swift/trainers/seq2seq_trainer.py", line 61, in evaluate
    res = super().evaluate(*args, **kwargs)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer_seq2seq.py", line 193, in evaluate
    return super().evaluate(eval_dataset, ignore_keys=ignore_keys, metric_key_prefix=metric_key_prefix)
           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 2603, in evaluate
    output = self.evaluation_loop(
        eval_dataloader,
    ...<5 lines>...
        metric_key_prefix=metric_key_prefix,
    )
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/transformers/trainer.py", line 2718, in evaluation_loop
    for step, inputs in enumerate(dataloader):
                        ~~~~~~~~~^^^^^^^^^^^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/accelerate/data_loader.py", line 582, in __iter__
    dataloader_iter = self.base_dataloader.__iter__()
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 505, in __iter__
    return self._get_iterator()
           ~~~~~~~~~~~~~~~~~~^^
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 438, in _get_iterator
    return _MultiProcessingDataLoaderIter(self)
  File "/home/sliedes/proj/archives/ocr_finetune/swiftwrap/.venv/lib/python3.14/site-packages/torch/utils/data/dataloader.py", line 1176, in __init__
    w.start()
    ~~~~~~~^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
                  ~~~~~~~~~~~^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/context.py", line 300, in _Popen
    return Popen(process_obj)
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_forkserver.py", line 35, in __init__
    super().__init__(process_obj)
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_fork.py", line 20, in __init__
    self._launch(process_obj)
    ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/popen_forkserver.py", line 47, in _launch
    reduction.dump(process_obj, buf)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/home/sliedes/.local/share/uv/python/cpython-3.14.3-linux-x86_64-gnu/lib/python3.14/multiprocessing/reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
_pickle.PicklingError: Can't pickle local object <function PreTrainedModel.enable_input_require_grads.<locals>.make_inputs_require_grads at 0x7d8bfce966c0>
when serializing collections.OrderedDict item 2
when serializing collections.OrderedDict object
when serializing dict item '_forward_hooks'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionPatchEmbed state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionPatchEmbed object
when serializing dict item 'patch_embed'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionModel state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5VisionModel object
when serializing dict item 'visual'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5Model state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5Model object
when serializing dict item 'model'
when serializing dict item '_modules'
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration state
when serializing transformers.models.qwen3_5.modeling_qwen3_5.Qwen3_5ForConditionalGeneration object
when serializing dict item 'model'
when serializing dict item '_modules'
when serializing peft.tuners.lora.model.LoraModel state
when serializing peft.tuners.lora.model.LoraModel object
when serializing dict item 'base_model'
when serializing dict item '_modules'
when serializing peft.peft_model.PeftModelForCausalLM state
when serializing peft.peft_model.PeftModelForCausalLM object
when serializing tuple item 0
when serializing list item 0
when serializing dict item '_handles'
when serializing swift.template.templates.qwen.Qwen3_5Template state
when serializing swift.template.templates.qwen.Qwen3_5Template object
when serializing tuple item 0
when serializing method reconstructor arguments
when serializing method object
when serializing dict item 'encode_func'
when serializing swift.dataset.utils.LazyLLMDataset state
when serializing swift.dataset.utils.LazyLLMDataset object
when serializing tuple item 1
when serializing dict item '_args'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object

Thanks for testing! Let me check

@tastelikefeet

Copy link
Copy Markdown
Collaborator

Seems I missed some attributes in __getstate__, and fixed in #10000

@sliedes

sliedes commented Aug 28, 2026

Copy link
Copy Markdown
Author

Seems I missed some attributes in __getstate__, and fixed in #10000

Yes, this seems to fix it for me!

Side note, feel free to ignore: I did need to update my single-line JPEG XL support plugin because with forkserver it was no longer loaded in the data loaders, but I think this is more about me perhaps abusing the swift plugin mechanism here a bit. With fork, having https://github.com/Isotr0py/pillow-jpegxl-plugin installed in the environment and doing simply import pillow_jxl in the single-line swift plugin made swift support loading JPEG XL images. Now I needed to add another line, and it obviously still wouldn't work with spawn:

import multiprocessing
import pillow_jxl

multiprocessing.set_forkserver_preload(["pillow_jxl"])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants