@@ -37,11 +37,12 @@ const (
3737//go:embed schemas/github-workflow.json
3838var githubWorkflowSchema string
3939
40- // formatCompilerError creates a formatted compiler error message
40+ // formatCompilerError creates a formatted compiler error message with optional error wrapping
4141// filePath: the file path to include in the error (typically markdownPath or lockFile)
4242// errType: the error type ("error" or "warning")
4343// message: the error message text
44- func formatCompilerError (filePath string , errType string , message string ) error {
44+ // cause: optional underlying error to wrap (use nil for validation errors)
45+ func formatCompilerError (filePath string , errType string , message string , cause error ) error {
4546 formattedErr := console .FormatError (console.CompilerError {
4647 Position : console.ErrorPosition {
4748 File : filePath ,
@@ -51,6 +52,13 @@ func formatCompilerError(filePath string, errType string, message string) error
5152 Type : errType ,
5253 Message : message ,
5354 })
55+
56+ // Wrap the underlying error if provided (preserves error chain)
57+ if cause != nil {
58+ return fmt .Errorf ("%s: %w" , formattedErr , cause )
59+ }
60+
61+ // Create new error for validation errors (no underlying cause)
5462 return errors .New (formattedErr )
5563}
5664
@@ -84,8 +92,8 @@ func (c *Compiler) CompileWorkflow(markdownPath string) error {
8492 // Already formatted, return as-is
8593 return err
8694 }
87- // Otherwise, create a basic formatted error
88- return formatCompilerError (markdownPath , "error" , err .Error ())
95+ // Otherwise, create a basic formatted error with wrapping
96+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
8997 }
9098
9199 return c .CompileWorkflowData (workflowData , markdownPath )
@@ -97,7 +105,7 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
97105 // Validate expression safety - check that all GitHub Actions expressions are in the allowed list
98106 log .Printf ("Validating expression safety" )
99107 if err := validateExpressionSafety (workflowData .MarkdownContent ); err != nil {
100- return formatCompilerError (markdownPath , "error" , err .Error ())
108+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
101109 }
102110
103111 // Validate expressions in runtime-import files at compile time
@@ -107,13 +115,13 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
107115 githubDir := filepath .Dir (workflowDir ) // .github
108116 workspaceDir := filepath .Dir (githubDir ) // repo root
109117 if err := validateRuntimeImportFiles (workflowData .MarkdownContent , workspaceDir ); err != nil {
110- return formatCompilerError (markdownPath , "error" , err .Error ())
118+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
111119 }
112120
113121 // Validate feature flags
114122 log .Printf ("Validating feature flags" )
115123 if err := validateFeatures (workflowData ); err != nil {
116- return formatCompilerError (markdownPath , "error" , err .Error ())
124+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
117125 }
118126
119127 // Check for action-mode feature flag override
@@ -122,7 +130,7 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
122130 if actionModeStr , ok := actionModeVal .(string ); ok && actionModeStr != "" {
123131 mode := ActionMode (actionModeStr )
124132 if ! mode .IsValid () {
125- return formatCompilerError (markdownPath , "error" , fmt .Sprintf ("invalid action-mode feature flag '%s'. Must be 'dev', 'release', or 'script'" , actionModeStr ))
133+ return formatCompilerError (markdownPath , "error" , fmt .Sprintf ("invalid action-mode feature flag '%s'. Must be 'dev', 'release', or 'script'" , actionModeStr ), nil )
126134 }
127135 log .Printf ("Overriding action mode from feature flag: %s" , mode )
128136 c .SetActionMode (mode )
@@ -133,7 +141,7 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
133141 // Validate dangerous permissions
134142 log .Printf ("Validating dangerous permissions" )
135143 if err := validateDangerousPermissions (workflowData ); err != nil {
136- return formatCompilerError (markdownPath , "error" , err .Error ())
144+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
137145 }
138146
139147 // Validate agent file exists if specified in engine config
@@ -145,25 +153,25 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
145153 // Validate sandbox configuration
146154 log .Printf ("Validating sandbox configuration" )
147155 if err := validateSandboxConfig (workflowData ); err != nil {
148- return formatCompilerError (markdownPath , "error" , err .Error ())
156+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
149157 }
150158
151159 // Validate safe-outputs target configuration
152160 log .Printf ("Validating safe-outputs target fields" )
153161 if err := validateSafeOutputsTarget (workflowData .SafeOutputs ); err != nil {
154- return formatCompilerError (markdownPath , "error" , err .Error ())
162+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
155163 }
156164
157165 // Validate safe-outputs allowed-domains configuration
158166 log .Printf ("Validating safe-outputs allowed-domains" )
159167 if err := c .validateSafeOutputsAllowedDomains (workflowData .SafeOutputs ); err != nil {
160- return formatCompilerError (markdownPath , "error" , err .Error ())
168+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
161169 }
162170
163171 // Validate network allowed domains configuration
164172 log .Printf ("Validating network allowed domains" )
165173 if err := c .validateNetworkAllowedDomains (workflowData .NetworkPermissions ); err != nil {
166- return formatCompilerError (markdownPath , "error" , err .Error ())
174+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
167175 }
168176
169177 // Emit experimental warning for sandbox-runtime feature
@@ -207,7 +215,7 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
207215 if len (validationResult .MissingPermissions ) > 0 {
208216 if c .strictMode {
209217 // In strict mode, missing permissions are errors
210- return formatCompilerError (markdownPath , "error" , message )
218+ return formatCompilerError (markdownPath , "error" , message , nil )
211219 } else {
212220 // In non-strict mode, missing permissions are warnings
213221 fmt .Fprintln (os .Stderr , formatCompilerMessage (markdownPath , "warning" , message ))
@@ -226,7 +234,7 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
226234
227235 // Validate that all allowed tools have their toolsets enabled
228236 if err := ValidateGitHubToolsAgainstToolsets (allowedTools , enabledToolsets ); err != nil {
229- return formatCompilerError (markdownPath , "error" , err .Error ())
237+ return formatCompilerError (markdownPath , "error" , err .Error (), err )
230238 }
231239
232240 // Print informational message if "projects" toolset is explicitly specified
@@ -258,14 +266,14 @@ func (c *Compiler) validateWorkflowData(workflowData *WorkflowData, markdownPath
258266 message += "permissions:\n "
259267 message += " actions: read"
260268
261- return formatCompilerError (markdownPath , "error" , message )
269+ return formatCompilerError (markdownPath , "error" , message , nil )
262270 }
263271 }
264272
265273 // Validate dispatch-workflow configuration (independent of agentic-workflows tool)
266274 log .Print ("Validating dispatch-workflow configuration" )
267275 if err := c .validateDispatchWorkflow (workflowData , markdownPath ); err != nil {
268- return formatCompilerError (markdownPath , "error" , fmt .Sprintf ("dispatch-workflow validation failed: %v" , err ))
276+ return formatCompilerError (markdownPath , "error" , fmt .Sprintf ("dispatch-workflow validation failed: %v" , err ), err )
269277 }
270278
271279 return nil
@@ -277,15 +285,15 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP
277285 // Generate the YAML content
278286 yamlContent , err := c .generateYAML (workflowData , markdownPath )
279287 if err != nil {
280- return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("failed to generate YAML: %v" , err ))
288+ return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("failed to generate YAML: %v" , err ), err )
281289 }
282290
283291 // Always validate expression sizes - this is a hard limit from GitHub Actions (21KB)
284292 // that cannot be bypassed, so we validate it unconditionally
285293 log .Print ("Validating expression sizes" )
286294 if err := c .validateExpressionSizes (yamlContent ); err != nil {
287295 // Store error first so we can write invalid YAML before returning
288- formattedErr := formatCompilerError (markdownPath , "error" , fmt .Sprintf ("expression size validation failed: %v" , err ))
296+ formattedErr := formatCompilerError (markdownPath , "error" , fmt .Sprintf ("expression size validation failed: %v" , err ), err )
289297 // Write the invalid YAML to a .invalid.yml file for inspection
290298 invalidFile := strings .TrimSuffix (lockFile , ".lock.yml" ) + ".invalid.yml"
291299 if writeErr := os .WriteFile (invalidFile , []byte (yamlContent ), 0644 ); writeErr == nil {
@@ -298,7 +306,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP
298306 log .Print ("Validating for template injection vulnerabilities" )
299307 if err := validateNoTemplateInjection (yamlContent ); err != nil {
300308 // Store error first so we can write invalid YAML before returning
301- formattedErr := formatCompilerError (markdownPath , "error" , err .Error ())
309+ formattedErr := formatCompilerError (markdownPath , "error" , err .Error (), err )
302310 // Write the invalid YAML to a .invalid.yml file for inspection
303311 invalidFile := strings .TrimSuffix (lockFile , ".lock.yml" ) + ".invalid.yml"
304312 if writeErr := os .WriteFile (invalidFile , []byte (yamlContent ), 0644 ); writeErr == nil {
@@ -312,7 +320,7 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP
312320 log .Print ("Validating workflow against GitHub Actions schema" )
313321 if err := c .validateGitHubActionsSchema (yamlContent ); err != nil {
314322 // Store error first so we can write invalid YAML before returning
315- formattedErr := formatCompilerError (markdownPath , "error" , fmt .Sprintf ("workflow schema validation failed: %v" , err ))
323+ formattedErr := formatCompilerError (markdownPath , "error" , fmt .Sprintf ("workflow schema validation failed: %v" , err ), err )
316324 // Write the invalid YAML to a .invalid.yml file for inspection
317325 invalidFile := strings .TrimSuffix (lockFile , ".lock.yml" ) + ".invalid.yml"
318326 if writeErr := os .WriteFile (invalidFile , []byte (yamlContent ), 0644 ); writeErr == nil {
@@ -333,19 +341,19 @@ func (c *Compiler) generateAndValidateYAML(workflowData *WorkflowData, markdownP
333341 // Validate runtime packages (npx, uv)
334342 log .Print ("Validating runtime packages" )
335343 if err := c .validateRuntimePackages (workflowData ); err != nil {
336- return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("runtime package validation failed: %v" , err ))
344+ return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("runtime package validation failed: %v" , err ), err )
337345 }
338346
339347 // Validate firewall configuration (log-level enum)
340348 log .Print ("Validating firewall configuration" )
341349 if err := c .validateFirewallConfig (workflowData ); err != nil {
342- return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("firewall configuration validation failed: %v" , err ))
350+ return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("firewall configuration validation failed: %v" , err ), err )
343351 }
344352
345353 // Validate repository features (discussions, issues)
346354 log .Print ("Validating repository features" )
347355 if err := c .validateRepositoryFeatures (workflowData ); err != nil {
348- return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("repository feature validation failed: %v" , err ))
356+ return "" , formatCompilerError (markdownPath , "error" , fmt .Sprintf ("repository feature validation failed: %v" , err ), err )
349357 }
350358 } else if c .verbose {
351359 fmt .Fprintln (os .Stderr , console .FormatWarningMessage ("Schema validation available but skipped (use SetSkipValidation(false) to enable)" ))
@@ -377,7 +385,7 @@ func (c *Compiler) writeWorkflowOutput(lockFile, yamlContent string, markdownPat
377385 // Only write if content has changed
378386 if ! contentUnchanged {
379387 if err := os .WriteFile (lockFile , []byte (yamlContent ), 0644 ); err != nil {
380- return formatCompilerError (lockFile , "error" , fmt .Sprintf ("failed to write lock file: %v" , err ))
388+ return formatCompilerError (lockFile , "error" , fmt .Sprintf ("failed to write lock file: %v" , err ), err )
381389 }
382390 log .Print ("Lock file written successfully" )
383391 }
0 commit comments