{"record":{"id":"3e44935d246d7d70","repo":"mrdoob/three.js","slug":"no-ui-for-primitivename-ndx-param-name","errorCode":null,"errorMessage":"no ui for ${primitiveName}:${ndx} param: ${name}","messagePattern":"no ui for (.+?):(.+?) param: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"manual/resources/threejs-primitives.js","lineNumber":1095,"sourceCode":"\t\t\t\t[ ...shape.querySelectorAll( 'span.com' ) ]\n\t\t\t\t\t.filter( span => span.textContent.indexOf( '// ui:' ) >= 0 )\n\t\t\t\t\t.forEach( ( span ) => {\n\n\t\t\t\t\t\tconst nameRE = /ui: ([a-zA-Z0-9_]+) *$/;\n\t\t\t\t\t\tconst name = nameRE.exec( span.textContent )[ 1 ];\n\t\t\t\t\t\tspan.textContent = '';\n\t\t\t\t\t\tif ( ! info.ui ) {\n\n            console.error(`no ui for ${primitiveName}:${ndx}`);  // eslint-disable-line\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t// throw new Error(`no ui for ${primitiveName}:${ndx}`);\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst ui = info.ui[ name ];\n\t\t\t\t\t\tif ( ! ui ) {\n\n\t\t\t\t\t\t\tthrow new Error( `no ui for ${primitiveName}:${ndx} param: ${name}` );\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst valueElem = getValueElem( span );\n\t\t\t\t\t\tif ( ! valueElem ) {\n\n            console.error(`no value element for ${primitiveName}:${ndx} param: ${name}`);  // eslint-disable-line\n\t\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst inputHolderHolder = document.createElement( 'div' );\n\t\t\t\t\t\tinputHolderHolder.className = 'input';\n\t\t\t\t\t\tconst inputHolder = document.createElement( 'div' );\n\t\t\t\t\t\tspan.appendChild( inputHolderHolder );\n\t\t\t\t\t\tinputHolderHolder.appendChild( inputHolder );\n\t\t\t\t\t\tswitch ( ui.type ) {\n","sourceCodeStart":1077,"sourceCodeEnd":1113,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/manual/resources/threejs-primitives.js#L1077-L1113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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 } }.","Verify the name in '// ui: NAME' exactly matches (case-sensitive) the key in info.ui.","If the parameter should not have a UI control, remove the '// ui: NAME' comment from the code listing."],"exampleFix":"// code listing has: // ui: width\n// before\nconst info = {\n  create: makeBox,\n  ui: {\n    height: { type: 'range', min: 1, max: 10 },\n  },\n};\n\n// after — add the missing entry for the named param\nconst info = {\n  create: makeBox,\n  ui: {\n    width: { type: 'range', min: 1, max: 10 },\n    height: { type: 'range', min: 1, max: 10 },\n  },\n};","handlingStrategy":"validation","validationCode":"// Before building UI, assert every '// ui: name' comment has a matching info.ui entry.\nfunction assertUiCovers( info, commentNames ) {\n  if ( ! info.ui ) return; // missing-ui is a soft (console.error) path\n  const missing = commentNames.filter( n => ! info.ui[ n ] );\n  if ( missing.length ) throw new Error( `info.ui missing params: ${missing.join( ', ' )}` );\n}","typeGuard":"function isUiEntry( v ) {\n  return v !== null && typeof v === 'object' &&\n    ( v.type === 'range' || v.type === 'bool' || v.type === 'text' );\n}","tryCatchPattern":"const ui = info.ui && info.ui[ name ];\nif ( ! ui ) { console.warn( `no ui for ${primitiveName}:${ndx} param ${name}; skipping` ); return; }","preventionTips":["Keep '// ui: NAME' comments and info.ui keys identical (case-sensitive).","When you add a parameter, add both the comment and the ui entry in the same change.","Treat a missing info.ui entry as a build-time error in a lint step."],"tags":["manual","primitives","ui","lesson"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}