{"record":{"id":"01bbeb0c98c9df82","repo":"mastra-ai/mastra","slug":"malformed-referencepath","errorCode":null,"errorMessage":"Malformed referencePath","messagePattern":"Malformed referencePath","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"packages/server/src/server/handlers/workspace.ts","lineNumber":1081,"sourceCode":"      if (!skills) {\n        throw new HTTPException(404, { message: 'No workspace with skills configured' });\n      }\n\n      // Refresh skills with request context (handles dynamic skill resolvers)\n      await skills.maybeRefresh({ requestContext });\n\n      // Resolve skill to get its name for the response\n      const skill = await skills.get(identifier);\n      if (!skill) {\n        throw new HTTPException(404, { message: `Skill \"${identifier}\" not found` });\n      }\n\n      // Decode the reference path (it may be URL encoded)\n      let decodedPath: string;\n      try {\n        decodedPath = decodeURIComponent(referencePath);\n      } catch {\n        throw new HTTPException(400, { message: 'Malformed referencePath' });\n      }\n\n      // Prevent path traversal via the reference path parameter\n      assertSafeFilePath(decodedPath);\n\n      // getReference expects a path relative to skill.path, so prepend 'references/'\n      // since the URL path already contains the literal /references/ segment\n      const content = await skills.getReference(identifier, `references/${decodedPath}`);\n      if (content === null) {\n        throw new HTTPException(404, {\n          message: `Reference \"${decodedPath}\" not found in skill \"${identifier}\"`,\n        });\n      }\n\n      return {\n        skillName: skill.name,\n        referencePath: decodedPath,\n        content,","sourceCodeStart":1063,"sourceCodeEnd":1099,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/handlers/workspace.ts#L1063-L1099","documentation":"The referencePath parameter may be URL-encoded, so the handler decodes it with decodeURIComponent. If decoding fails (invalid percent sequences such as a stray '%'), it throws a 400 'Malformed referencePath'.","triggerScenarios":"Passing a referencePath containing an invalid percent-encoding sequence, e.g. `%ZZ` or a lone `%`, in the URL.","commonSituations":"Double-encoding bugs in client code; manually concatenating Windows paths with '%' characters into URLs; frameworks partially encoding the path so '%' survives unescaped.","solutions":["URL-encode the reference path on the client with encodeURIComponent before inserting it into the URL.","Remove or escape stray '%' characters in the path (literal percent must be sent as %25).","If double-encoding is suspected, send the path encoded exactly once."],"exampleFix":"// before\nconst url = `/skills/my-skill/references/${path}` // path = 'a%b.md'\n// after\nconst url = `/skills/my-skill/references/${encodeURIComponent(path)}`;","handlingStrategy":"validation","validationCode":"let encoded: string;\ntry {\n  encoded = encodeURIComponent(referencePath);\n} catch {\n  throw new Error('referencePath cannot be encoded');\n}\nif (/%(?![0-9A-Fa-f]{2})/.test(referencePath)) {\n  throw new Error('referencePath contains a stray % — encode it as %25');\n}","typeGuard":"function isPercentSafePath(p: string): boolean {\n  return !/%(?![0-9A-Fa-f]{2})/.test(p);\n}","tryCatchPattern":"try {\n  return await client.getSkillReference({ skillName, referencePath });\n} catch (e) {\n  if (isHttpException(e, 400) && String(e.message).includes('Malformed')) {\n    throw new Error('Fix client encoding: use encodeURIComponent exactly once');\n  }\n  throw e;\n}","preventionTips":["Encode path segments with encodeURIComponent exactly once.","Never hand-build URLs from raw filesystem paths.","Test URL builders with paths containing %, spaces, and unicode."],"tags":["http-400","url-encoding","validation","skills"],"backgroundTag":"malformed-url-encoding","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}