{"record":{"id":"adc2f89d2443ad29","repo":"docling-project/docling","slug":"invalid-orientation-angle-expected-values-in","errorCode":null,"errorMessage":"invalid orientation {angle}, expected values in: {sorted(CLIPPED_ORIENTATIONS)}","messagePattern":"invalid orientation (.+?), expected values in: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/utils/orientation.py","lineNumber":53,"sourceCode":"        r_x2 = r_x1\n        r_y2 = r_y1 + height\n        r_x3 = r_x0\n        r_y3 = r_y2\n    elif angle == 270:\n        r_x0 = im_h - (top + height)\n        r_y0 = left\n        r_x1 = r_x0\n        r_y1 = r_y0 + width\n        r_x2 = r_x1 + height\n        r_y2 = r_y1\n        r_x3 = r_x2\n        r_y3 = r_y0\n    else:\n        msg = (\n            f\"invalid orientation {angle}, expected values in:\"\n            f\" {sorted(CLIPPED_ORIENTATIONS)}\"\n        )\n        raise ValueError(msg)\n    rectangle = BoundingRectangle(\n        r_x0=r_x0,\n        r_y0=r_y0,\n        r_x1=r_x1,\n        r_y1=r_y1,\n        r_x2=r_x2,\n        r_y2=r_y2,\n        r_x3=r_x3,\n        r_y3=r_y3,\n        coord_origin=CoordOrigin.TOPLEFT,\n    )\n    return rectangle\n","sourceCodeStart":35,"sourceCodeEnd":66,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/utils/orientation.py#L35-L66","documentation":"Raised in docling/utils/orientation.py when applying an orientation/rotation angle to build a BoundingRectangle and the angle is not one of the clipped orientations {0, 90, 180, 270}. The function has explicit branches that permute the rectangle's corner coordinates for each supported 90-degree rotation; any other angle falls into the else branch and raises ValueError because no geometry is defined for it.","triggerScenarios":"Calling the orientation application function in orientation.py (e.g. when rotating bounding rectangles for page orientation corrections) with an angle such as 45, 360, -90, or 90.5 — anything outside CLIPPED_ORIENTATIONS.","commonSituations":"Passing raw rotation-detection output (which may be arbitrary degrees) directly into rectangle rotation; normalizing angles incorrectly (e.g. forgetting % 360 so 360 or negative values slip through); mixing clockwise/counterclockwise conventions so 270 vs -90 mismatch occurs.","solutions":["Normalize and snap the angle first: snapped = round(angle / 90) * 90 % 360, then pass snapped.","Check angle in (0, 90, 180, 270) before calling and log/skip if not, so one bad page does not abort a batch conversion.","Trace where the angle comes from — orientation classifiers should already emit only the four valid classes; if not, fix the upstream producer."],"exampleFix":"# before\nrect = apply_orientation(rect, angle=detect_angle(page))  # ValueError if 45.0\n\n# after\nangle = round(detect_angle(page) / 90) * 90 % 360\nif angle in (0, 90, 180, 270):\n    rect = apply_orientation(rect, angle=angle)","handlingStrategy":"validation","validationCode":"angle = round(angle / 90) * 90 % 360\nif angle not in (0, 90, 180, 270):\n    raise ValueError(f\"angle {angle} not supported for rectangle rotation\")\nrect = apply_orientation(rect, angle=angle)","typeGuard":"def is_clipped_orientation(angle: int | float) -> bool:\n    return angle in (0, 90, 180, 270)","tryCatchPattern":"try:\n    rect = apply_orientation(rect, angle)\nexcept ValueError:\n    logger.warning(\"dropping unsupported orientation %s for item\", angle)\n    return rect  # keep unrotated geometry","preventionTips":["Normalize every rotation angle with round(a / 90) * 90 % 360 before geometry ops.","Treat orientation as an enum of four classes, not a continuous value, at API boundaries.","Log and skip bad angles per item so one bad detection does not kill a batch conversion."],"tags":["orientation","geometry","validation"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}