{"record":{"id":"6f03e0856fd0ab32","repo":"sgl-project/sglang","slug":"resolution-already-failed-on-this-serverargs-the","errorCode":null,"errorMessage":"resolution already failed on this ServerArgs; the handlers that ran left their writes on the record, and a second pass would read that partial output as fresh input. Build a new record from the corrected arguments.","messagePattern":"resolution already failed on this ServerArgs; the handlers that ran left their writes on the record, and a second pass would read that partial output as fresh input\\. Build a new record from the corrected arguments\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/server_args.py","lineNumber":3690,"sourceCode":"        resolve it itself -- stays raw.\n        \"\"\"\n\n    def resolve_once(self) -> None:\n        \"\"\"Run the resolution pipeline, unless this record has been through it.\n\n        Resolution is a deterministic function of the raw inputs -- two records\n        built from the same arguments declare the same things -- but the\n        handlers do not survive a second pass over their own output: DP\n        attention halves ``chunked_prefill_size`` again on every re-entry.\n\n        The publishing entry of every process calls this. In a child the record\n        arrived by pickle and brought its declarations along, so the child has\n        nothing left to derive and projects what the parent decided.\n        \"\"\"\n        if getattr(self, \"_resolution_finished\", False):\n            return\n        if getattr(self, \"_resolution_failed\", False):\n            raise RuntimeError(\n                \"resolution already failed on this ServerArgs; the handlers that \"\n                \"ran left their writes on the record, and a second pass would \"\n                \"read that partial output as fresh input. Build a new record \"\n                \"from the corrected arguments.\"\n            )\n        try:\n            self._run_resolution_pipeline()\n        except BaseException:\n            # The handlers that ran already declared, and they are not\n            # idempotent over their own output.\n            self._resolution_failed = True\n            raise\n        # Set here too, because the dummy/absent-model path returns before the\n        # end of the pipeline that normally sets it: the gate is about whether\n        # the handlers ran, not how far they got.\n        self._resolution_finished = True\n\n    def resolved_dict(self) -> Dict[str, Any]:","sourceCodeStart":3672,"sourceCodeEnd":3708,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/server_args.py#L3672-L3708","documentation":"ServerArgs resolution (the pipeline of _handle_* handlers that derives defaults and cross-checks fields) is one-shot: if it fails midway, some handlers have already mutated the ServerArgs record. resolve_once() refuses a second pass so that partial writes are never mistaken for user-supplied input, and demands a fresh ServerArgs built from corrected arguments.","triggerScenarios":"Catching a ValueError from server launch (e.g. inside launch_or_reuse_server or a test harness), mutating the same ServerArgs object to fix the field, and calling resolve/launch again on it.","commonSituations":"Retry loops in deployment scripts or tests that catch a config error, patch the offending attribute, and relaunch with the same object; in-process embeddings/server helpers that reuse ServerArgs across attempts.","solutions":["Construct a brand-new ServerArgs (or a deep copy of the pristine pre-resolution arguments) with the corrected values and resolve that.","If retrying programmatically, keep the original kwargs dict and rebuild ServerArgs from it on each attempt.","Never reuse an instance whose resolution previously raised."],"exampleFix":"# before\nargs = ServerArgs(model=\"m\", tp_size=99)\ntry:\n    run(args)\nexcept ValueError:\n    args.tp_size = 1\n    run(args)  # RuntimeError: resolution already failed\n# after\nkwargs = {\"model\": \"m\", \"tp_size\": 99}\ntry:\n    run(ServerArgs(**kwargs))\nexcept ValueError:\n    run(ServerArgs(**{**kwargs, \"tp_size\": 1}))","handlingStrategy":"fallback","validationCode":"# keep pristine kwargs and always rebuild\nserver_kwargs = {\"model\": \"m\", \"tp_size\": 8}\ndef build_args():\n    return ServerArgs(**server_kwargs)  # fresh record each attempt","typeGuard":null,"tryCatchPattern":"try:\n    resolve(server_args)\nexcept (ValueError, RuntimeError) as e:\n    log.warning('launch failed: %s; rebuilding ServerArgs', e)\n    server_args = ServerArgs(**corrected_kwargs)  # never patch+retry the old object","preventionTips":["Treat ServerArgs as single-use once resolution has run.","Store the original kwargs dict, not the object, for retries."],"tags":["server-args","resolution-pipeline","state-corruption","sglang"],"backgroundTag":"invalid-object-reuse-after-failure","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}