{"record":{"id":"1f8fc3e4d1e582a8","repo":"dagger/dagger","slug":"workspace-working-directory-q-must-be-a-relative","errorCode":null,"errorMessage":"workspace working directory %q must be a relative path within the workspace root","messagePattern":"workspace working directory %q must be a relative path within the workspace root","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/schema/workspace.go","lineNumber":1814,"sourceCode":"}\n\nfunc (s *workspaceSchema) withWorkdir(\n\tctx context.Context,\n\tparent dagql.ObjectResult[*core.Workspace],\n\targs struct {\n\t\tPath string\n\t},\n) (dagql.ObjectResult[*core.Workspace], error) {\n\tsrv, err := core.CurrentDagqlServer(ctx)\n\tif err != nil {\n\t\treturn dagql.ObjectResult[*core.Workspace]{}, err\n\t}\n\t// Public schema surface: keep the working directory inside the workspace root.\n\t// cleanWorkspaceRelPath is only filepath.Clean, so reject absolute paths and\n\t// anything escaping via \"..\".\n\tcwd := cleanWorkspaceRelPath(args.Path)\n\tif filepath.IsAbs(args.Path) || cwd == \"..\" || strings.HasPrefix(cwd, \"..\"+string(filepath.Separator)) {\n\t\treturn dagql.ObjectResult[*core.Workspace]{}, fmt.Errorf(\"workspace working directory %q must be a relative path within the workspace root\", args.Path)\n\t}\n\tws := parent.Self().Clone()\n\tws.Cwd = cwd\n\treturn dagql.NewObjectResultForCurrentCall(ctx, srv, ws)\n}\n\ntype workspaceWithMountedDirectoryArgs struct {\n\tPath   string\n\tSource core.DirectoryID\n}\n\nfunc (s *workspaceSchema) withMountedDirectory(\n\tctx context.Context,\n\tparent dagql.ObjectResult[*core.Workspace],\n\targs workspaceWithMountedDirectoryArgs,\n) (dagql.ObjectResult[*core.Workspace], error) {\n\treturn withMountedSource(ctx, parent, args.Path, args.Source, \"withDirectory\")\n}","sourceCodeStart":1796,"sourceCodeEnd":1832,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/schema/workspace.go#L1796-L1832","documentation":"Workspace.withWorkdir validates that the requested working directory stays inside the workspace root. After cleaning the path, absolute paths, the value \"..\", or any path escaping via a \"../\" prefix are rejected with this explicit validation message. It is a deliberate public-schema guard: the working directory must remain a relative path within the workspace.","triggerScenarios":"Calling Workspace.withWorkdir with an absolute path (e.g. \"/src\" or \"C:\\\\src\"), with \"..\", or with a path like \"../outside\".","commonSituations":"Reusing workdir values meant for a shell/container (absolute paths) instead of workspace-relative ones; computing paths by joining with the host cwd; path traversal attempts to read outside the workspace.","solutions":["Pass a workspace-relative path (e.g. \"src/app\" instead of \"/src/app\").","Strip or convert any absolute prefix before calling withWorkdir.","Reject or normalize paths containing \"..\" before invoking the API.","Use filepath.ToSlash/clean on the input and confirm it does not start with the separator or \"..\"."],"exampleFix":"// before\nawait ws.withWorkdir(\"/src/app\")\n// error: workspace working directory \"/src/app\" must be a relative path...\n\n// after\nawait ws.withWorkdir(\"src/app\")","handlingStrategy":"validation","validationCode":"function assertWorkspaceRelPath(p) {\n  const clean = p.replace(/\\\\/g, \"/\").replace(/(^|\\/)\\.\\.?($|\\/)/g, \"/\")\n    .split(\"/\").filter(Boolean).join(\"/\")\n  if (!p || p.startsWith(\"/\") || p === \"..\" || p.startsWith(\"../\") || clean.startsWith(\"..\")) {\n    throw new Error(`workdir must be relative within workspace: ${p}`)\n  }\n  return clean || \".\"\n}","typeGuard":"const isSafeWorkdir = (p) => typeof p === 'string' && p.length > 0 && !p.startsWith('/') && p !== '..' && !p.startsWith('../');","tryCatchPattern":"try {\n  ws = await ws.withWorkdir(path)\n} catch (e) {\n  if (String(e.message).includes(\"must be a relative path within the workspace root\")) {\n    // normalize path to workspace-relative and retry\n  }\n  throw e\n}","preventionTips":["Always pass workspace-relative paths to withWorkdir","Strip host-specific absolute prefixes before the call","Resolve and reject \"..\" segments in caller-supplied paths"],"tags":["validation","workdir","path","workspace"],"backgroundTag":"path-escapes-root","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}