Skip to content

fix(viewer): guard texture.repeat against non-array values (Sentry MONOREPO-EDITOR-EK)#504

Merged
Aymericr merged 2 commits into
mainfrom
fix/sentry-EDITOR-EK
Jul 19, 2026
Merged

fix(viewer): guard texture.repeat against non-array values (Sentry MONOREPO-EDITOR-EK)#504
Aymericr merged 2 commits into
mainfrom
fix/sentry-EDITOR-EK

Conversation

@anton-pascal

@anton-pascal anton-pascal commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Bug

Sentry MONOREPO-EDITOR-EK: TypeError: t.repeat?.join is not a function — crashes on /editor/:projectId during slab geometry material cache-key building.

Root cause

packages/core/src/schema/material.ts:36 types texture.repeat as z.tuple([z.number(), z.number()]).optional() — an array when present. But at runtime some material data has repeat present as a non-array value (legacy scalar, or a THREE.Vector2-like object). In packages/viewer/src/lib/materials.ts (getTextureKey()), the code did:

const repeat = texture.repeat?.join('x') ?? 'default'

The ?. optional-chain only guards against null/undefined — it does not guard against a value of the wrong type. When repeat is present but not an array, .join is undefined and calling it throws.

Fix

Added an explicit Array.isArray type guard before calling .join, falling back to the raw value (or 'default') otherwise:

const repeat = Array.isArray(texture.repeat)
  ? texture.repeat.join('x')
  : (texture.repeat ?? 'default')

Behavior for the normal (array) case is unchanged.

Risk

Low. This value is only used to build a cache-key string (getTextureKey) — no rendering/material logic changes. Single-line-scope edit, no refactor.

Do not merge without review.


Note

Low Risk
Viewer-only defensive parsing of material texture metadata; behavior for valid tuple repeats is unchanged, while legacy shapes now get consistent repeat values instead of crashing.

Overview
Fixes editor crashes when texture.repeat is present but not a tuple (legacy scalar or { x, y } values) by stopping use of .join on repeat in getTextureKey.

Introduces resolveTextureRepeat to coerce tuple, scalar, and Vector2-shaped repeats into [x, y], with scale (or 1) as fallback for malformed values. getTextureKey and getTexture both use this helper so cache keys match the UV repeat applied to loaded textures.

Adds Bun tests for normalization, scale fallback, and distinct cache keys for different Vector2-shaped repeats.

Reviewed by Cursor Bugbot for commit c40517d. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread packages/viewer/src/lib/materials.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c40517d. Configure here.

if (isFiniteNumber(x) && isFiniteNumber(y)) return [x, y]
}
return [fallback, fallback]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Incomplete tuple repeats use scale

Medium Severity

When texture.repeat is an array with only one finite element, resolveTextureRepeat rejects the whole value and applies scale on both axes. The previous path used each index with per-axis scale fallback, so U/V tiling can change and different materials can share the same getTextureKey and createMaterial cache entry.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c40517d. Configure here.

@Aymericr
Aymericr merged commit 16ea186 into main Jul 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants