{"record":{"id":"40c409886591d822","repo":"hcengineering/platform","slug":"missing-workspace","errorCode":null,"errorMessage":"Missing workspace","messagePattern":"Missing workspace","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"pods/preview/src/middleware.ts","lineNumber":80,"sourceCode":"\n    next()\n  } catch (err: any) {\n    next(err)\n  }\n}\n\n/**\n * Validates blob route params and ensures the caller's token grants access to\n * the workspace taken from the URL. Must run after `withAuthorization`, which\n * guarantees a token is present.\n */\nexport const withBlob = (req: RequestWithAuth, res: Response, next: NextFunction): void => {\n  try {\n    const workspace = req.params.workspace\n    const name = req.params.name\n\n    if (workspace === undefined || workspace === '') {\n      throw new HttpError(400, 'Missing workspace')\n    }\n    if (name === undefined || name === '') {\n      throw new HttpError(400, 'Missing blob name')\n    }\n\n    const token = req.token\n    if (token == null) {\n      throw new HttpError(401, 'Unauthorized')\n    }\n\n    const hasWorkspaceAccess =\n      (token.workspace as string) === workspace || token.account === systemAccountUuid || token.extra?.admin === 'true'\n    if (!hasWorkspaceAccess) {\n      throw new HttpError(401, 'Unauthorized')\n    }\n\n    next()\n  } catch (err: any) {","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/pods/preview/src/middleware.ts#L62-L98","documentation":"withBlob reads req.params.workspace and req.params.name and throws HttpError 400 'Missing workspace' when the workspace param is undefined or empty. It is a request-shape validation guard before blob access.","triggerScenarios":"Hitting a blob endpoint without a :workspace route parameter, or with workspace='' (empty segment).","commonSituations":"Client URL template missing the workspace segment (e.g. /blob//name); router path misconfigured so params aren't bound; programmatic fetch building URL with undefined interpolated.","solutions":["Include the workspace name in the URL path (e.g. /blob/{workspace}/{name})","Ensure the route registration matches the client's URL shape so params populate","Guard on the client: encodeURIComponent(workspace) before building the URL","Check for empty-string interpolation when workspace is dynamic"],"exampleFix":"// before\nfetch(`/blob/${workspace}/logo.png`)\n// after\nif (!workspace) throw new Error('workspace is required')\nfetch(`/blob/${encodeURIComponent(workspace)}/logo.png`)","handlingStrategy":"validation","validationCode":"function blobUrl (workspace: string, name: string): string {\n  if (!workspace) throw new Error('workspace is required')\n  if (!name) throw new Error('blob name is required')\n  return `/blob/${encodeURIComponent(workspace)}/${encodeURIComponent(name)}`\n}","typeGuard":"function isNonEmptyString (v: unknown): v is string {\n  return typeof v === 'string' && v.length > 0\n}","tryCatchPattern":"try {\n  const res = await fetch(blobUrl(ws, name), { headers: authHeaders() })\n  if (res.status === 400) {\n    // bad request shape: verify workspace/name params\n  }\n} catch (err) { /* handle */ }","preventionTips":["Build blob URLs through a single helper that validates and encodes params","Validate route params in client-side forms before navigation/fetch","Confirm server route patterns define :workspace and :name consistently","Avoid string concatenation with possibly-undefined variables in URLs"],"tags":["http","validation","params"],"backgroundTag":"missing-required-parameter","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}