{"record":{"id":"17ff695dbfc01021","repo":"invoke-ai/InvokeAI","slug":"str-e","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":422,"severity":"warning","filePath":"invokeai/app/api/routers/app_info.py","lineNumber":271,"sourceCode":"\n@app_router.patch(\n    \"/runtime_config\",\n    operation_id=\"update_runtime_config\",\n    status_code=200,\n    response_model=InvokeAIAppConfigWithSetFields,\n)\ndef update_runtime_config(\n    _: AdminUserOrDefault,\n    changes: UpdateAppGenerationSettingsRequest = Body(description=\"Writable runtime configuration changes\"),\n) -> InvokeAIAppConfigWithSetFields:\n    # The request model validates the *shape* of generation_devices; also verify the devices exist\n    # on this machine before persisting, so we can't write a config that fails on the next startup\n    # (e.g. 'cuda:99' on a 2-GPU box). Same resolution the startup path uses.\n    if changes.generation_devices is not None:\n        try:\n            TorchDevice.get_generation_devices(changes.generation_devices)\n        except ValueError as e:\n            raise HTTPException(status_code=422, detail=str(e))\n    with _EXTERNAL_PROVIDER_CONFIG_LOCK:\n        config = get_config()\n        update_dict = changes.model_dump(exclude_unset=True)\n        config.update_config(update_dict)\n\n        if config.config_file_path.exists():\n            persisted_config = load_and_migrate_config(config.config_file_path)\n        else:\n            persisted_config = DefaultInvokeAIAppConfig()\n\n        persisted_config.update_config(update_dict)\n        persisted_config.write_file(config.config_file_path)\n        return InvokeAIAppConfigWithSetFields(set_fields=config.model_fields_set, config=_redact_config_secrets(config))\n\n\n@app_router.get(\n    \"/external_providers/status\",\n    operation_id=\"get_external_provider_statuses\",","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/app_info.py#L253-L289","documentation":"This is a 422 Unprocessable Entity raised by the PATCH/PUT runtime-config endpoint when the requested generation devices (e.g. torch device strings like 'cuda:99') cannot be resolved by TorchDevice.get_generation_devices. InvokeAI pre-validates device changes with the same resolution logic as the startup path so a bad config can never be persisted and break the next startup. The detail is str(e) from the underlying ValueError, typically naming the invalid device.","triggerScenarios":"PATCHing runtime config with generation_devices containing an invalid/unavailable torch device string (typo, nonexistent CUDA index like 'cuda:99' on a 2-GPU box, CPU-only machine given 'cuda:0').","commonSituations":"Copying config from a multi-GPU machine to a smaller box; typo in device name ('cuda' vs 'cuda:0' vs 'gpu'); Docker container without GPU passthrough; driver/CUDA mismatch making a device invisible to torch.","solutions":["Read the detail message to identify the offending device string","Use a valid torch device available on this machine (e.g. 'cuda:0', 'mps', 'cpu'); verify with python -c \"import torch; print(torch.cuda.device_count())\"","Send only the fields you intend to change (exclude_unset) instead of echoing devices copied from another machine","Remove the generation_devices key from the request if you don't mean to change devices"],"exampleFix":"// before\n{ \"generation_devices\": { \"denoising\": \"cuda:99\", \"clip\": \"cuda:99\" } }\n// after\n{ \"generation_devices\": { \"denoising\": \"cuda:0\", \"clip\": \"cuda:0\" } }","handlingStrategy":"validation","validationCode":"import torch\nfrom invokeai.backend.util.devices import TorchDevice\n\ndef validate_generation_devices(devices: dict) -> None:\n    # mirror the server's pre-check before sending the config\n    TorchDevice.get_generation_devices(devices)  # raises ValueError if invalid","typeGuard":"def is_valid_device(name: str) -> bool:\n    import torch\n    if name == 'auto':\n        return True\n    base = name.split(':')[0]\n    if base == 'cuda':\n        idx = int(name.split(':')[1]) if ':' in name else 0\n        return torch.cuda.is_available() and idx < torch.cuda.device_count()\n    return base in ('mps', 'cpu')","tryCatchPattern":"try:\n    resp = requests.patch(f'{base}/app/config', json=payload)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if e.response.status_code == 422:\n        print('Invalid device:', e.response.json()['detail'])","preventionTips":["Enumerate available torch devices on the target machine before sending device strings","Never copy device configs between machines without validating CUDA indices","Send only changed fields (exclude_unset semantics)","Test config changes against a dev instance first"],"tags":["http-422","config","gpu-device"],"backgroundTag":"invalid-device-selection","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}