{"record":{"id":"ad2cf0b7fbab65c1","repo":"freeCodeCamp/freeCodeCamp","slug":"no-challenge-files-provided","errorCode":null,"errorMessage":"No challenge files provided","messagePattern":"No challenge files provided","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/challenge-builder/src/build.ts","lineNumber":321,"sourceCode":"      .join('\\n'),\n    sources: buildSourceMap(finalFiles),\n    error\n  };\n}\n\nfunction buildBackendChallenge({ url, challengeType }: BuildChallengeData) {\n  return {\n    challengeType,\n    build: '',\n    sources: { contents: url }\n  };\n}\n\nasync function buildPythonChallenge({\n  challengeFiles,\n  challengeType\n}: BuildChallengeData): Promise<BuildResult> {\n  if (!challengeFiles) throw new Error('No challenge files provided');\n  const pipeLine = composeFunctions(\n    ...(getPythonTransformers() as unknown as ApplyFunctionProps[])\n  );\n  const finalFiles = await Promise.all(challengeFiles.map(pipeLine));\n  const error = finalFiles.find(({ error }) => error)?.error;\n  const sources = buildSourceMap(finalFiles);\n\n  return {\n    challengeType,\n    sources,\n    build: sources?.contents,\n    error\n  };\n}\n","sourceCodeStart":303,"sourceCodeEnd":336,"githubUrl":"https://github.com/freeCodeCamp/freeCodeCamp/blob/4289125977fa5bc4c90d7e2860bc04062a30abee/packages/challenge-builder/src/build.ts#L303-L336","documentation":"Thrown by buildPythonChallenge in the challenge-builder package when it is dispatched without source files. Python challenges are built by piping every entry of challengeFiles through getPythonTransformers(), so the function guards its input first and throws synchronously when challengeFiles is undefined or null rather than producing an empty build. BuildChallengeData types the field as optional (challengeFiles?), so TypeScript does not flag the missing field at the call site.","triggerScenarios":"Calling buildChallenge or buildPythonChallenge with an object whose challengeFiles key is absent, null, or undefined - for example buildPythonChallenge({ challengeType: 28 }). In practice: the curriculum or database record for a Python challenge ships no files, a data-loading step drops or renames the field (files vs challengeFiles), or a newly routed Python challengeType reaches this builder although its data shape has no files.","commonSituations":"Adding a new Python-based challengeType to the dispatcher without confirming its curriculum objects carry files; ingesting challenge JSON whose property is named differently; destructuring or optional-chaining mistakes producing undefined. The sibling buildJsChallenge has the same guard (build.ts:284), so refactors often hit both.","solutions":["Pass the sources in: buildPythonChallenge({ challengeType, challengeFiles }) with a populated ChallengeFile[] (fileKey, path, contents).","Log the incoming challenge object right before dispatch when challengeFiles is missing, to identify which curriculum record is malformed.","Check the upstream mapping that builds BuildChallengeData - confirm the key name is challengeFiles and the array survives parsing.","If missing files is legitimate for some types, branch in the dispatcher before calling buildPythonChallenge instead of relying on the throw."],"exampleFix":"// before\nbuildPythonChallenge({ challengeType });\n// after\nbuildPythonChallenge({ challengeType, challengeFiles });","handlingStrategy":"validation","validationCode":"const files = challenge.challengeFiles;\nif (!Array.isArray(files) || files.length === 0) {\n  throw new Error('Python challenge ' + challenge.id + ' has no challengeFiles');\n}\nreturn buildPythonChallenge({\n  challengeType: challenge.challengeType,\n  challengeFiles: files\n});","typeGuard":"type BuildablePythonData = BuildChallengeData & {\n  challengeFiles: NonNullable<BuildChallengeData['challengeFiles']>;\n};\n\nconst hasPythonFiles = (\n  data: BuildChallengeData\n): data is BuildablePythonData => Array.isArray(data.challengeFiles);","tryCatchPattern":"try {\n  const result = await buildChallenge(challenge);\n} catch (err) {\n  if (err instanceof Error && err.message === 'No challenge files provided') {\n    logger.warn({ id: challenge.id }, 'skipped: challenge has no files');\n    continue;\n  }\n  throw err;\n}","preventionTips":["Make challengeFiles required in the type the Python builder accepts, so the compiler flags every caller missing files.","Validate challenge definitions with a schema (typebox or zod) that requires challengeFiles for Python challengeTypes at ingestion.","Include the challenge id when re-throwing so batch builds identify the offending record.","Unit-test the dispatcher with a files-less challenge to assert it fails fast with a clear message."],"tags":["challenge-builder","python","typescript","missing-argument","validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"4289125977fa5bc4c90d7e2860bc04062a30abee","analyzedAt":"2026-08-24T06:46:05.087Z","schemaVersion":2},"datasetVersion":"2026-08-24T07:17:09.176Z"}