{"record":{"id":"669fd345ea3a5b50","repo":"keras-team/keras","slug":"channels-are-allowed-and-the-first-and-last-dimens","errorCode":null,"errorMessage":"Channels are allowed and the first and last dimensions.","messagePattern":"Channels are allowed and the first and last dimensions\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/legacy/preprocessing/image.py","lineNumber":1810,"sourceCode":"    # 1. x must 2D image with one or more channels (i.e., a 3D tensor)\n    # 2. channels must be either first or last dimension\n    if np.unique([row_axis, col_axis, channel_axis]).size != 3:\n        raise ValueError(\n            \"'row_axis', 'col_axis', and 'channel_axis' must be distinct\"\n        )\n\n    # shall we support negative indices?\n    valid_indices = set([0, 1, 2])\n    actual_indices = set([row_axis, col_axis, channel_axis])\n    if actual_indices != valid_indices:\n        raise ValueError(\n            f\"Invalid axis' indices: {actual_indices - valid_indices}\"\n        )\n\n    if x.ndim != 3:\n        raise ValueError(\"Input arrays must be multi-channel 2D images.\")\n    if channel_axis not in [0, 2]:\n        raise ValueError(\n            \"Channels are allowed and the first and last dimensions.\"\n        )\n\n    transform_matrix = None\n    if theta != 0:\n        theta = np.deg2rad(theta)\n        rotation_matrix = np.array(\n            [\n                [np.cos(theta), -np.sin(theta), 0],\n                [np.sin(theta), np.cos(theta), 0],\n                [0, 0, 1],\n            ]\n        )\n        transform_matrix = rotation_matrix\n\n    if tx != 0 or ty != 0:\n        shift_matrix = np.array([[1, 0, tx], [0, 1, ty], [0, 0, 1]])\n        if transform_matrix is None:","sourceCodeStart":1792,"sourceCodeEnd":1828,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/legacy/preprocessing/image.py#L1792-L1828","documentation":"The legacy affine transform code can only move the channel axis to the first or last position of the 3D tensor because it transposes with hardcoded layouts. If channel_axis is 1 (channels in the middle) it raises this ValueError. The message itself contains a Keras typo ('and' should read 'in').","triggerScenarios":"Calling apply_affine_transform with channel_axis=1, e.g. row_axis=0, col_axis=2, channel_axis=1, an arrangement the implementation cannot handle.","commonSituations":"Programmatically permuting axes for exotic memory layouts, or adapting someone else's augmentation snippet and swapping the axis order incorrectly.","solutions":["Use channel_axis=2 (channels-last, the common case) or channel_axis=0 (channels-first)","If channels really sit in the middle, transpose first: img = np.moveaxis(img, 1, -1) and pass channel_axis=2"],"exampleFix":"# before\napply_affine_transform(img, theta=10, row_axis=0, col_axis=2, channel_axis=1)\n# after\nimg_moved = np.moveaxis(img, 1, -1)\napply_affine_transform(img_moved, theta=10, row_axis=0, col_axis=1, channel_axis=2)","handlingStrategy":"validation","validationCode":"if channel_axis not in (0, 2):\n    img = np.moveaxis(img, channel_axis, -1)\n    channel_axis = 2","typeGuard":"def supported_channel_axis(ch):\n    return ch in (0, 2)","tryCatchPattern":null,"preventionTips":["Standardize data to channels-last before augmentation"],"tags":["keras","preprocessing","image","axis-order"],"backgroundTag":"invalid-argument-value","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}