{"record":{"id":"f333dd251ba6d795","repo":"Crosstalk-Solutions/project-nomad","slug":"filename-is-required","errorCode":null,"errorMessage":"Filename is required","messagePattern":"Filename is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"admin/app/services/docs_service.ts","lineNumber":68,"sourceCode":"\n      // Filter out attribute-undefined errors which may be caused by emojis and special characters\n      const criticalErrors = errors.filter((e) => e.error.id !== 'attribute-undefined')\n      if (criticalErrors.length > 0) {\n        logger.error('Markdoc validation errors:', errors.map((e) => JSON.stringify(e.error)).join(', '))\n        throw new Error('Markdoc validation failed')\n      }\n\n      return Markdoc.transform(ast, config)\n    } catch (error) {\n      logger.error('Error parsing Markdoc content:', error)\n      throw new InternalServerErrorException(`Error parsing content: ${(error as Error).message}`)\n    }\n  }\n\n  async parseFile(_filename: string) {\n    try {\n      if (!_filename) {\n        throw new Error('Filename is required')\n      }\n\n      const filename = _filename.endsWith('.md') ? _filename : `${_filename}.md`\n\n      // Prevent path traversal — resolved path must stay within the docs directory\n      const basePath = path.resolve(this.docsPath)\n      const fullPath = path.resolve(path.join(this.docsPath, filename))\n      if (!fullPath.startsWith(basePath + path.sep)) {\n        throw new Error('Invalid document slug')\n      }\n\n      const fileExists = await getFileStatsIfExists(fullPath)\n      if (!fileExists) {\n        throw new Error(`File not found: ${filename}`)\n      }\n\n      const fileStream = await getFile(fullPath, 'stream')\n      if (!fileStream) {","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/Crosstalk-Solutions/project-nomad/blob/0bd1c6f4f9888d577fe232de06ac144bb8337131/admin/app/services/docs_service.ts#L50-L86","documentation":"Thrown by DocsService.parseFile when called with an empty/undefined filename. The method immediately requires a truthy _filename before resolving it against the docs directory. Note this is a plain Error that gets wrapped into 'Error parsing file: ...' by the enclosing catch.","triggerScenarios":"Calling parseFile(''), parseFile(undefined), or parseFile(null) — e.g. a route handler where the slug path parameter is optional and was omitted.","commonSituations":"Optional route params not validated at the controller level, undefined slug from a frontend that builds the URL from an empty state, destructuring mistakes passing the wrong variable.","solutions":["Make the slug required in the controller (ParseSlugPipe / @IsNotEmpty validation) so NestJS rejects it before the service","Default or reject empty slugs at the call site before invoking parseFile","Fix the caller that produces undefined (check frontend URL construction)"],"exampleFix":"// before\n@Get(':slug')\ncontent(@Param('slug') slug?: string) {\n  return this.docs.content(slug) // may pass undefined\n}\n\n// after\n@Get(':slug')\ncontent(@Param('slug', ParseSlugPipe) slug: string) {\n  return this.docs.content(slug)\n}","handlingStrategy":"validation","validationCode":"if (!slug || typeof slug !== 'string' || slug.trim() === '') {\n  throw new BadRequestException('slug is required')\n}\nawait docsService.parseFile(slug)","typeGuard":"const isValidSlugRequest = (slug: unknown): slug is string =>\n  typeof slug === 'string' && slug.trim().length > 0","tryCatchPattern":"try {\n  await docsService.parseFile(slug)\n} catch (e) {\n  if ((e as Error).message.includes('Filename is required'))\n    throw new BadRequestException('Document slug is required')\n  throw e\n}","preventionTips":["Make the slug a required route param with a validation pipe","Add class-validator @IsNotEmpty() on DTO fields feeding parseFile","Write a unit test for the empty-slug path"],"tags":["validation","required-parameter","docs","nestjs"],"backgroundTag":"missing-required-parameter","analyzedSha":"0bd1c6f4f9888d577fe232de06ac144bb8337131","analyzedAt":"2026-08-27T05:34:15.424Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}