{"record":{"id":"81ea22ee2a2a26b8","repo":"BabylonJS/Babylon.js","slug":"const-line-not-found","errorCode":null,"errorMessage":"\"Const line not found\"","messagePattern":"\"Const line not found\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dev/smartFilters/src/utils/buildTools/shaderConverter.ts","lineNumber":210,"sourceCode":"    for (const matches of constGroups) {\r\n        const annotationJSON = matches[1];\r\n\r\n        // If it doesn't have any annotation, treat it as a regular const\r\n        if (!annotationJSON) {\r\n            continue;\r\n        }\r\n\r\n        const annotation = JSON.parse(annotationJSON.replace(\"//\", \"\").trim()) as ConstMetadataAnnotation;\r\n\r\n        // If the annotation doesn't have a \"property\" field, treat it as a regular const\r\n        if (!annotation.property) {\r\n            continue;\r\n        }\r\n\r\n        const constLine = matches[2];\r\n\r\n        if (!constLine) {\r\n            throw new Error(\"Const line not found\");\r\n        }\r\n\r\n        const constLineMatches = new RegExp(/^const\\s+(\\w+)\\s+(\\w+)\\s*=\\s*([^\\s;]+)\\s*;?\\s*$/gm).exec(constLine);\r\n        if (!constLineMatches || constLineMatches.length < 4) {\r\n            throw new Error(`Consts must have a name, type, and a default value: '${constLine}'`);\r\n        }\r\n        const type = constLineMatches[1];\r\n        const friendlyName = constLineMatches[2];\r\n        const defaultValue = constLineMatches[3];\r\n\r\n        if (!friendlyName) {\r\n            throw new Error(`Consts must have a name: '${constLine}'`);\r\n        }\r\n        if (defaultValue === null) {\r\n            throw new Error(`Consts must have a value: '${constLine}'`);\r\n        }\r\n\r\n        const constProperty: ConstPropertyMetadata | null =\r","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/smartFilters/src/utils/buildTools/shaderConverter.ts#L192-L228","documentation":"ParseFragmentShader in the smartFilters shader converter throws this while iterating const declarations that carry a {\"property\":...} annotation. The regex matched a 'const' group, but capture group 2 (the const line text itself) is empty. It is a defensive invariant check — it almost always means the annotation/const pairing in the shader source is malformed rather than a real empty const line.","triggerScenarios":"Calling ParseFragmentShader (via fragmentShaderInfo/result) on a fragment shader where a // {\"property\":...} annotation is present but the immediately following const declaration cannot be captured by the regex /(\\/\\/\\s*\\{.*\\}\\s*(?:\\r\\n|\\r|\\n)+)?(const .*)/gm (e.g. annotation separated from a 'const' keyword by intervening code, or the const text was mangled by preprocessing).","commonSituations":"Hand-edited shaders where the annotation comment and the const line got separated or merged; shader strings built programmatically with wrong line endings; minified/stripped shaders where the const line was removed but the annotation comment left behind; copy-paste of annotations without their const.","solutions":["Ensure every // {\"property\":...} annotation is immediately followed (next line) by a full `const <type> <name> = <value>;` declaration.","Keep the annotation and its const in the same block and do not run code strippers/minifiers on the shader source before conversion.","If the const should not be exposed as a property, remove the 'property' field from the annotation (or the whole annotation) so the parser skips it.","Add the const line back explicitly, e.g. `const float myVal = 0.5;` right after the annotation."],"exampleFix":"// before\n// {\"property\": {\"type\": \"float\", \"options\": {...}}}\n\nfloat unrelated = 1.0;\n// after\n// {\"property\": {\"type\": \"float\", \"options\": {...}}}\nconst float brightness = 0.5;","handlingStrategy":"validation","validationCode":"const annotated = shader.match(/\\/\\/\\s*\\{.*\\}\\s*\\n\\s*const .*/g);\nif (!annotated || annotated.some(m => !/^\\/\\/\\s*\\{.*\\}\\s*\\n\\s*const \\w+ \\w+ = [^\\s;]+;?\\s*$/m.test(m))) {\n  throw new Error(\"Every property annotation must be immediately followed by a single-line const declaration\");\n}","typeGuard":"function hasConstAfterAnnotation(shader: string): boolean {\n  return [...shader.matchAll(/(\\/\\/\\s*\\{.*\\}\\s*(?:\\r?\\n)+)(const .*)/gm)].every(m => typeof m[2] === \"string\" && m[2].length > 0);\n}","tryCatchPattern":"try {\n  const info = ParseFragmentShader(blockName, namespace, shader);\n} catch (e) {\n  if (e instanceof Error && e.message === \"Const line not found\") {\n    // fix the annotation/const pairing in the shader source and retry once\n  } else throw e;\n}","preventionTips":["Always place property annotations on the line directly above their const declaration.","Never run minifiers or code strippers on shader source before ParseFragmentShader.","Keep a unit test that converts every shipped shader at build time.","Validate shaders with a lint rule that pairs // {...} annotations with following const lines."],"tags":["shader","parsing","glsl","build-tools"],"backgroundTag":"shader-const-annotation-malformed","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}