mrdoob/three.js · error · Error

no ui for ${primitiveName}:${ndx} param: ${name}

Error message

no ui for ${primitiveName}:${ndx} param: ${name}

What it means

Thrown while building interactive UI for a primitives demo: the prettyprinted code contains a '// ui: paramName' comment, the primitive's info DOES have a ui object, but ui[paramName] is undefined. Note the sibling check at line 1084 (entire info.ui missing) was deliberately downgraded from a throw to console.error, so a fully missing ui is now non-fatal — but a missing entry for one named param is still a hard throw. The param name is parsed from the trailing token of the '// ui:' comment.

Source

Thrown at manual/resources/threejs-primitives.js:1095

				[ ...shape.querySelectorAll( 'span.com' ) ]
					.filter( span => span.textContent.indexOf( '// ui:' ) >= 0 )
					.forEach( ( span ) => {

						const nameRE = /ui: ([a-zA-Z0-9_]+) *$/;
						const name = nameRE.exec( span.textContent )[ 1 ];
						span.textContent = '';
						if ( ! info.ui ) {

            console.error(`no ui for ${primitiveName}:${ndx}`);  // eslint-disable-line
							return;
							// throw new Error(`no ui for ${primitiveName}:${ndx}`);

						}

						const ui = info.ui[ name ];
						if ( ! ui ) {

							throw new Error( `no ui for ${primitiveName}:${ndx} param: ${name}` );

						}

						const valueElem = getValueElem( span );
						if ( ! valueElem ) {

            console.error(`no value element for ${primitiveName}:${ndx} param: ${name}`);  // eslint-disable-line
							return;

						}

						const inputHolderHolder = document.createElement( 'div' );
						inputHolderHolder.className = 'input';
						const inputHolder = document.createElement( 'div' );
						span.appendChild( inputHolderHolder );
						inputHolderHolder.appendChild( inputHolder );
						switch ( ui.type ) {

View on GitHub (pinned to da05705fa3)

Solutions

  1. Open the failing primitive's info object and add a ui entry for the named param, e.g. ui: { width: { type: 'range', min, max, mult, precision } }.
  2. Verify the name in '// ui: NAME' exactly matches (case-sensitive) the key in info.ui.
  3. If the parameter should not have a UI control, remove the '// ui: NAME' comment from the code listing.

Example fix

// code listing has: // ui: width
// before
const info = {
  create: makeBox,
  ui: {
    height: { type: 'range', min: 1, max: 10 },
  },
};

// after — add the missing entry for the named param
const info = {
  create: makeBox,
  ui: {
    width: { type: 'range', min: 1, max: 10 },
    height: { type: 'range', min: 1, max: 10 },
  },
};
Defensive patterns

Strategy: validation

Validate before calling

// Before building UI, assert every '// ui: name' comment has a matching info.ui entry.
function assertUiCovers( info, commentNames ) {
  if ( ! info.ui ) return; // missing-ui is a soft (console.error) path
  const missing = commentNames.filter( n => ! info.ui[ n ] );
  if ( missing.length ) throw new Error( `info.ui missing params: ${missing.join( ', ' )}` );
}

Type guard

function isUiEntry( v ) {
  return v !== null && typeof v === 'object' &&
    ( v.type === 'range' || v.type === 'bool' || v.type === 'text' );
}

Try / catch

const ui = info.ui && info.ui[ name ];
if ( ! ui ) { console.warn( `no ui for ${primitiveName}:${ndx} param ${name}; skipping` ); return; }

Prevention

When it happens

Trigger: Authoring a primitive whose code comments declare '// ui: width' but whose info.ui map only defines entries for other params (or has a typo'd key). Renaming a constructor parameter and its '// ui:' comment but forgetting to rename the ui map key.

Common situations: Editing the primitives lesson: you add a new controllable parameter, annotate the code with '// ui: newParam', generate the geometry, but do not add a ui.newParam = { type, min, max, ... } entry. Case mismatch between the comment token and the ui key.

Related errors


AI-assisted analysis of mrdoob/three.js@da05705fa3 (2026-08-12). Data as JSON: /api/errors/3e44935d246d7d70. Report an issue: GitHub.