Skip to content

fix(reshard): validate published elsize against its dtype - #620

Open
nicolasnoble wants to merge 2 commits into
mainfrom
nnoble/mx-444-reshard-elsize-validation
Open

fix(reshard): validate published elsize against its dtype#620
nicolasnoble wants to merge 2 commits into
mainfrom
nnoble/mx-444-reshard-elsize-validation

Conversation

@nicolasnoble

@nicolasnoble nicolasnoble commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

The reshard shard table carries dtype and elsize as independent fields and nothing reconciled them. elsize is what the slice plan does address arithmetic with, src_addr = addr + offset * elsize and nbytes = n * elsize, so a publisher emitting a byte width that disagrees with its dtype silently produced reads at the wrong offset and the wrong length. plan_pull already compares source and destination dtypes; the byte width behind it was checked against nothing.

build_sources now rejects a tensor whose elsize disagrees with its resolved dtype's itemsize, and merge_shard_tables includes elsize in the cross-rank agreement check, which its docstring already described it as doing. Both checks sit on the consumer side, since encode and decode stay dependency-free and a publish-side check would cover only the in-tree publishers. _torch_dtype also now requires its label to resolve to an actual torch.dtype; it was a bare getattr, so torch.load resolved to a function and was carried forward as a dtype.

Nothing in the repo can trigger this today, since both in-tree publishers derive elsize from tensor.element_size(). It seems worth guarding anyway because the publish path is the documented trainer integration contract, and elsize is a required field on it with no invariant tying it to dtype. The six added tests were confirmed failing against the unpatched module.

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation of tensor data types and element sizes during shard planning and merging.
    • Detects and reports inconsistent shard shapes, data types, or element sizes across ranks.
    • Rejects unsupported data type labels with clearer validation errors.
  • Tests

    • Added coverage for data type validation, element-size mismatches, inconsistent shard metadata, and successful shard merging.

The shard table carries dtype and elsize as independent fields and nothing
reconciled them. elsize drives raw address arithmetic in the slice plan
(src_addr = addr + offset * elsize, nbytes = n * elsize), so a publisher
emitting a byte width that disagrees with its dtype produced reads at the
wrong offset and the wrong length instead of an error.

- build_sources now rejects a tensor whose elsize disagrees with its
  resolved dtype's itemsize.
- merge_shard_tables now includes elsize in the cross-rank agreement check,
  which its docstring already claimed it did.
- _torch_dtype requires the label to resolve to a torch.dtype. It previously
  accepted any string naming a torch attribute, so "torch.load" resolved to
  a function and was carried as a dtype.

Signed-off-by: Nicolas 'Pixel' Noble <nicolas@nobis-crew.org>
@copy-pr-bot
copy-pr-bot Bot deployed to automated-release August 12, 2026 23:33 Active
@copy-pr-bot
copy-pr-bot Bot deployed to automated-release August 12, 2026 23:33 Active
@github-actions github-actions Bot added the fix label Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The reshard rendezvous now validates Torch dtype labels, tensor element sizes, and cross-rank shard metadata. Tests cover valid normalization, invalid attributes, inconsistent metadata, and successful shard merging.

Changes

Reshard metadata validation

Layer / File(s) Summary
Dtype and source validation
modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py, modelexpress_client/python/tests/test_reshard_refit_rendezvous.py
_torch_dtype accepts supported label forms and rejects non-dtype or unknown attributes. build_sources rejects element sizes that do not match the resolved dtype. Tests cover these cases.
Shard-table consistency checks
modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py, modelexpress_client/python/tests/test_reshard_refit_rendezvous.py
merge_shard_tables checks element-size consistency with shape and dtype. Tests cover rejection of inconsistent tables and successful merging.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: 🟡 Moderate · up to 1183e

The PR adds useful elsize validation, but cross-rank merging can still reject valid shard tables when equivalent dtype labels use different prefixes, and malformed labels are not rejected consistently. Merge should wait for the label handling to be corrected.

Poem

I’m a rabbit with a tidy array,
Dtypes checked in proper array.
Element sizes match just right,
Shards now merge without a fight.
Hop, hop—metadata bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: validating published element sizes against their resolved dtype.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py`:
- Around line 240-247: Normalize or resolve dtype labels before the cross-rank
consistency check in the rendezvous merge path, so prefixed and unprefixed
producer forms compare equivalently while preserving shape and element-size
validation. Add a regression test in test_reshard_refit_rendezvous.py covering a
merge with mixed dtype label forms.

Apply the same fix in
`@modelexpress_client/python/tests/test_reshard_refit_rendezvous.py` around lines
328 - 331.
- Around line 178-181: Update decode_shard_table to validate that each dtype
label is a string before calling split("."); raise ValueError for non-string
labels, while preserving the existing unsupported-string validation and dtype
lookup behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 846dbdd7-04b0-46e8-8e5a-c0e4e8b97655

📥 Commits

Reviewing files that changed from the base of the PR and between 46da339 and 1183e1e.

📒 Files selected for processing (2)
  • modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py
  • modelexpress_client/python/tests/test_reshard_refit_rendezvous.py

Comment on lines +178 to +181
name = label.split(".")[-1]
dtype = getattr(torch, name, None)
if not isinstance(dtype, torch.dtype):
raise ValueError(f"unsupported dtype label {label!r} in shard table")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reject non-string dtype labels with ValueError.

decode_shard_table copies JSON dtype values without runtime type validation. A malformed table can therefore pass a non-string value to label.split("."), which raises AttributeError instead of rejecting the label as invalid metadata. Check the label type before splitting it.

Proposed fix
 def _torch_dtype(label: str):
     import torch
 
+    if not isinstance(label, str):
+        raise ValueError(f"unsupported dtype label {label!r} in shard table")
     name = label.split(".")[-1]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name = label.split(".")[-1]
dtype = getattr(torch, name, None)
if not isinstance(dtype, torch.dtype):
raise ValueError(f"unsupported dtype label {label!r} in shard table")
if not isinstance(label, str):
raise ValueError(f"unsupported dtype label {label!r} in shard table")
name = label.split(".")[-1]
dtype = getattr(torch, name, None)
if not isinstance(dtype, torch.dtype):
raise ValueError(f"unsupported dtype label {label!r} in shard table")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py` around
lines 178 - 181, Update decode_shard_table to validate that each dtype label is
a string before calling split("."); raise ValueError for non-string labels,
while preserving the existing unsupported-string validation and dtype lookup
behavior.

Comment on lines +240 to +247
if (
cur.full_shape != t.full_shape
or cur.dtype != t.dtype
or cur.elsize != t.elsize
):
raise ValueError(
f"tensor {t.name!r} published with inconsistent shape/dtype across ranks: "
f"{cur.full_shape}/{cur.dtype} vs {t.full_shape}/{t.dtype}"
f"tensor {t.name!r} published with inconsistent shape/dtype/elsize across ranks: "
f"{cur.full_shape}/{cur.dtype}/{cur.elsize} vs {t.full_shape}/{t.dtype}/{t.elsize}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Normalize dtype labels before cross-rank comparison.

The merge path compares raw labels even though the producer contract accepts prefixed and unprefixed forms.

  • modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py#L240-L247: compare resolved dtypes or canonicalized labels.
  • modelexpress_client/python/tests/test_reshard_refit_rendezvous.py#L328-L331: add a mixed-label merge regression test.
📍 Affects 2 files
  • modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py#L240-L247 (this comment)
  • modelexpress_client/python/tests/test_reshard_refit_rendezvous.py#L328-L331
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelexpress_client/python/modelexpress/refit/reshard/rendezvous.py` around
lines 240 - 247, Normalize or resolve dtype labels before the cross-rank
consistency check in the rendezvous merge path, so prefixed and unprefixed
producer forms compare equivalently while preserving shape and element-size
validation. Add a regression test in test_reshard_refit_rendezvous.py covering a
merge with mixed dtype label forms.

Apply the same fix in
`@modelexpress_client/python/tests/test_reshard_refit_rendezvous.py` around lines
328 - 331.

The publish contract accepts both the prefixed and unprefixed spelling
of a dtype label, but the cross-rank merge compared the raw strings, so
two ranks agreeing on a dtype could be reported as an inconsistency.
Compare on a canonical key instead.

- add _dtype_key, shared by the merge check and dtype resolution so the
  prefix handling cannot drift between them
- reject a non-string dtype label as invalid metadata rather than
  failing later on a missing attribute
- cover both with regression tests, plus a control that genuinely
  differing dtypes are still rejected

Signed-off-by: Nicolas 'Pixel' Noble <nicolas@nobis-crew.org>
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Aug 14, 2026
@copy-pr-bot
copy-pr-bot Bot deployed to automated-release August 14, 2026 17:34 Active
@copy-pr-bot
copy-pr-bot Bot deployed to automated-release August 14, 2026 17:34 Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant