Validation rules and common pitfalls
The loader enforces the rules below and reports the offending file + field on failure. The pitfalls list collects the mistakes that bite most often in real projects.
Contents
Section titled “Contents”Validation rules (cheat sheet)
Section titled “Validation rules (cheat sheet)”typeis required and must be one of the documented values.type: shellrequires exactly one ofcmd/argv;service_*requires exactly one ofcmd/argvplusservice.type: scriptrequires ascript:block in either simple (path) or phased (run+ optionalplan/cleanup) form.type: workflowrequires a non-emptysteps:and forbids type-specific fields (cmd,argv,service,script,workdir, etc.).type: builtinrequirescmd:(the builtin name) and rejects type-specific fields of other types (argv,script:,steps:,service,compose_args,workdir/workdir_from,user,mode,runner:).- Each workflow step has exactly one of
command/confirm/parallel;with/continue_on_errorare valid only on command steps (and on the container of aparallelblock — see Parallel sub-steps). - Env variable names must be unique across
params.*.env,context.*.env,files.*.env, and theenv:block. - File IDs must match
^[a-zA-Z_][a-zA-Z0-9_]*$. - File specs reject conflicting fields (e.g.
mkdiroutsidewrite,path+candidates,match/sortwithoutglob). workdir_fromis only valid forservice_exec/service_run/daemon.compose_argsis only valid forservice_exec/service_run/daemon.modeonservice_runmust be empty orrun.notify: trueis rejected ontype: daemon(error).notify: trueon a direct sub-step inside aparallel:block produces an info diagnostic; the runtime suppresses it.- An
args:block requires a${args}reference incmd:orargv:. Without one the block is inert (itsprefix/defaultwould never apply), so it is rejected at load rather than silently ignored. - In
argv:,${args}must be a whole element.--filter=${args}is rejected: arguments are spliced in as separate entries and nothing re-splits an embedded one. argv_append_fromis valid only forshell/service_exec/service_run, requiresargv:, and is rejected together withcmd:— appending computed values to a shell string would splice them into program text. It is rejected fortype: daemon(empty output means “skip”, which for a daemon reads as silently failing to start it) and for every other type (no argument vector to append to).argv_append_frommust not reference${args}. The pass-through arguments travel as positional parameters and are deliberately invisible to the expression; reference them fromargv:instead. See Computed arguments.
Common pitfalls
Section titled “Common pitfalls”- Don’t shell out to
./bin/dwe— usetype: dweor$DWE_BIN(in scripts). Either form picks up the running binary, even when the build path changes. - Don’t put secrets in
argv— useenv:so values are injected through the container env, not the command line. - Don’t reuse env names across sources — declaring
MYSQL_PWDin bothparams.x.envandenv:is a load-time error. - Don’t write to a path without
mkdir: true— write mode does not create parents on its own. - Don’t expect
${...}insideparams.*.default_from/context.*.from— those are plain dot-paths, not templated. - Don’t run a private command directly — reference it from a workflow or pipeline, or temporarily flip
private: falsefor debugging.