{"record":{"id":"7731967103fad135","repo":"sgl-project/sglang","slug":"expected-encoder-outputs-to-be-a-list-when-select","errorCode":null,"errorMessage":"Expected encoder_outputs to be a list when select_layers is provided","messagePattern":"Expected encoder_outputs to be a list when select_layers is provided","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/models/siglip2.py","lineNumber":396,"sourceCode":"    post_layer_norm: Optional[nn.LayerNorm],\n    select_layers: Optional[list[int]] = None,\n    max_possible_layers: Optional[int] = None,\n) -> torch.Tensor:\n    \"\"\"Resolve outputs from visual encoder based on select_layers.\"\"\"\n    if select_layers is None:\n        if isinstance(encoder_outputs, list):\n            encoder_outputs = encoder_outputs[-1]\n        if post_layer_norm is not None:\n            encoder_outputs = post_layer_norm(encoder_outputs)\n        return encoder_outputs\n\n    if max_possible_layers is None:\n        raise ValueError(\n            \"`max_possible_layers` must be provided alongside `select_layers`\"\n        )\n\n    if not isinstance(encoder_outputs, list):\n        raise ValueError(\n            \"Expected encoder_outputs to be a list when select_layers is provided\"\n        )\n\n    # Get the hidden states corresponding to the layer indices\n    num_loaded_layers = len(encoder_outputs) - 1\n    offset = max_possible_layers - num_loaded_layers\n    hs_pool = [\n        (\n            encoder_outputs[layer_idx]\n            if layer_idx >= 0\n            else encoder_outputs[layer_idx + offset]\n        )\n        for layer_idx in select_layers\n    ]\n\n    uses_last_layer = select_layers[-1] in (max_possible_layers - 1, -1)\n    if post_layer_norm is not None and uses_last_layer:\n        hs_pool[-1] = post_layer_norm(hs_pool[-1])","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/models/siglip2.py#L378-L414","documentation":"With select_layers, resolve_visual_encoder_outputs expects encoder_outputs to be the list of per-layer hidden states (length num_loaded_layers+1). If a single tensor (final output only) is passed, selected layer indices cannot be extracted and it raises this ValueError.","triggerScenarios":"Passing a final-hidden-states tensor instead of the per-layer output list when select_layers is set — e.g. the vision encoder was run without output_hidden_states=True equivalent, or the last-element shortcut at the top of the function already collapsed the list.","commonSituations":"Custom vision-tower wrappers that reuse this helper but call the encoder in a mode returning only the last tensor; upstream signature changes after refactors.","solutions":["Ensure the encoder is invoked in a mode that returns all per-layer outputs (list) when select_layers is used","Pass the raw list before any `encoder_outputs[-1]`-style reduction","Drop select_layers if only the final layer is needed"],"exampleFix":"# before\nfeats = resolve_visual_encoder_outputs(final_tensor, select_layers=[5,17], max_possible_layers=27)\n# after\nfeats = resolve_visual_encoder_outputs(all_layer_outputs, select_layers=[5,17], max_possible_layers=27)","handlingStrategy":"type-guard","validationCode":"assert isinstance(encoder_outputs, (list, tuple)), type(encoder_outputs)","typeGuard":"def is_layer_list(x): return isinstance(x, (list, tuple)) and all(t.ndim == 3 for t in x)","tryCatchPattern":null,"preventionTips":["Keep encoder in per-layer-output mode whenever select_layers is used"],"tags":["siglip2","api-misuse","layer-selection"],"backgroundTag":"wrong-argument-type","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}