{"record":{"id":"82681c8a3ce7b8c5","repo":"keras-team/keras","slug":"padding-valid-requires-output-size-to-equal-si-82681c","errorCode":null,"errorMessage":"`padding='valid'` requires output_size to equal size * grid. Got output_size=({H},{W}), grid=({gH},{gW}), size=({pH},{pW}).","messagePattern":"`padding='valid'` requires output_size to equal size \\* grid\\. Got output_size=\\((.+?),(.+?)\\), grid=\\((.+?),(.+?)\\), size=\\((.+?),(.+?)\\)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"keras/src/ops/image.py","lineNumber":1308,"sourceCode":"                f\"Got: gH={static_gH}, pH={pH}.\"\n            )\n        if isinstance(static_gW, int) and not (\n            static_gW * pW - pW < W <= static_gW * pW\n        ):\n            raise ValueError(\n                f\"For `padding='same'`, `output_size` width ({W}) must \"\n                f\"be in the range ((gW-1)*pW, gW*pW], i.e. \"\n                f\"({static_gW * pW - pW}, {static_gW * pW}]. \"\n                f\"Got: gW={static_gW}, pW={pW}.\"\n            )\n        pad_total_h = gH * pH - H\n        pad_total_w = gW * pW - W\n        begin = [0, pad_total_h // 2, pad_total_w // 2, 0]\n        out_shape = [B, H, W, C]\n        x = ops.slice(x, begin, out_shape)\n    else:\n        if gH * pH != H or gW * pW != W:\n            raise ValueError(\n                f\"`padding='valid'` requires output_size to equal \"\n                f\"size * grid. Got output_size=({H},{W}), \"\n                f\"grid=({gH},{gW}), size=({pH},{pW}).\"\n            )\n\n    if _unbatched:\n        x = backend.numpy.squeeze(x, axis=0)\n    return x\n\n\ndef _reconstruct_patches_3d(\n    patches,\n    size,\n    output_size,\n    strides=None,\n    padding=\"valid\",\n    data_format=None,\n):","sourceCodeStart":1290,"sourceCodeEnd":1326,"githubUrl":"https://github.com/keras-team/keras/blob/7a34a03db60bf60042242d6a556fc3be119046a5/keras/src/ops/image.py#L1290-L1326","documentation":"With padding='valid' (the default), reconstruct_patches (2D) tiles patches back with no padding to crop, so the result is exactly grid*patch per axis. The op raises when output_size != (gH*pH, gW*pW), since that size cannot be produced from this patch grid.","triggerScenarios":"reconstruct_patches(patches, size, output_size) (padding defaults to 'valid') with output_size not equal to (gH*pH, gW*pW), e.g. (B,4,4,64) patches, size=(8,8), output_size=(28,28) instead of (32,32).","commonSituations":"Forgetting the default is 'valid' and passing the original same-padded image size; extracting with padding='same' but reconstructing with 'valid'; using non-overlapping strides and assuming output equals original image size.","solutions":["Derive output_size from the patches tensor: (gH*pH, gW*pW) with gH,gW = patches.shape[1], patches.shape[2]","If patches were extracted with padding='same', pass padding='same' and the original image size","Verify strides: with strides<size the grid*patch exceeds the image and 'same' is required to crop"],"exampleFix":"# before\nreconstruct_patches(patches, size=(8,8), output_size=(28,28))\n\n# after\ngH, gW = patches.shape[1], patches.shape[2]\nreconstruct_patches(patches, size=(8,8), output_size=(gH*8, gW*8))","handlingStrategy":"validation","validationCode":"gH, gW = patches.shape[1], patches.shape[2]\noutput_size = (gH*size[0], gW*size[1])  # valid: derive, don't guess","typeGuard":"def valid_size_consistent(patches, size, output_size) -> bool:\n    return (patches.shape[1]*size[0], patches.shape[2]*size[1]) == tuple(output_size)","tryCatchPattern":null,"preventionTips":["Remember padding defaults to 'valid'","Derive output_size from patches.shape, never hardcode"],"tags":["keras","image","patches","shape-mismatch","input-validation"],"backgroundTag":"tensor-shape-mismatch","analyzedSha":"7a34a03db60bf60042242d6a556fc3be119046a5","analyzedAt":"2026-08-25T21:25:25.994Z","schemaVersion":2},"datasetVersion":"2026-08-26T02:17:13.382Z"}