Skip to content

Comments

return ErrInvalidComposeFile when yaml parsing fails#1936

Merged
jordanstephens merged 8 commits intomainfrom
jordan/avoid-swallowing-invalid-compose-file-error
Feb 18, 2026
Merged

return ErrInvalidComposeFile when yaml parsing fails#1936
jordanstephens merged 8 commits intomainfrom
jordan/avoid-swallowing-invalid-compose-file-error

Conversation

@jordanstephens
Copy link
Member

@jordanstephens jordanstephens commented Feb 18, 2026

Description

Since #1797, we are now loading the project name before loading the stack (in order to fetch remote stacks) this means that our previously implemented handler for compose loader errors is never reached, so we need to invoke it earlier. I am matching the error string for the yaml: prefix in order to trigger invoking the debugger, rather than always doing so, because there may be other errors that are not well handled by our debug agent. The compose-go (*ProjectOptions) LoadProject(context.Context) function does not return a specific error in this case, so I string matching is the best I can do.

Linked Issues

Checklist

  • I have performed a self-review of my code
  • I have added appropriate tests
  • I have updated the Defang CLI docs and/or README to reflect my changes, if necessary

Summary by CodeRabbit

  • Bug Fixes

    • Consolidated and improved handling of invalid compose files for clearer, more consistent feedback when loading projects.
  • New Features

    • Added an interactive debugging flow to assist diagnosing and recovering from compose file load errors.
  • Updates

    • Clarified the permission prompt text for the debugging assistant ("Defang AI Agent").

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Compose-load error handling was reorganized: compose command paths now call a unified handler; session loader invokes an interactive debug flow on compose-load errors; prompt text in the debugger was updated; a minor whitespace tweak in the loader.

Changes

Cohort / File(s) Summary
Compose CLI commands
src/cmd/cli/command/compose.go
Replaced the previous LoadProject error branch with a call to handleInvalidComposeFileErr(ctx, loadErr) and removed the in-file interactive debug flow and related imports.
Session loader / stack manager
src/cmd/cli/command/session.go
On compose load/name errors, routes to handleInvalidComposeFileErr(ctx, err) and implements an interactive debug flow: builds a debug project via compose.NewLoader().CreateProjectForDebug(), creates a debugger (debug.NewDebugger), and calls Debugger.DebugComposeLoadError when interactive.
Compose loader (minor)
src/pkg/cli/compose/loader.go
Whitespace/formatting adjustment in loadProject error handling; no behavioral change.
Debugger UX
src/pkg/debug/debug.go
Updated permission prompt from "Would you like to debug the deployment with AI?" to "Would you like to debug this with the Defang AI Agent?".

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI (compose command)
    participant Loader as Compose Loader
    participant Debugger as Defang Debugger

    User->>CLI: run compose command
    CLI->>Loader: LoadProject(...)
    Loader-->>CLI: return error (compose parse/validation)
    alt Interactive mode
        CLI->>Loader: CreateProjectForDebug()
        Loader-->>CLI: debug project
        CLI->>Debugger: NewDebugger(debug project)
        Debugger-->>CLI: debugger instance
        CLI->>Debugger: DebugComposeLoadError(debug config)
        Debugger->>User: interactive prompts and guidance
    else Non-interactive
        CLI-->>User: return original error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • lionello
  • KevyVo

Poem

🐇 I sniffed a YAML twist at dawn,
My whiskers twitched, I hopped along,
"Defang AI Agent, lend a paw," I said,
I nudge the debug thread, then hop ahead —
Compose loads mended, and I nap on. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: detecting when YAML parsing fails and returning the appropriate error instead of allowing it to be swallowed.
Linked Issues check ✅ Passed The PR addresses issue #1931 by detecting YAML parsing errors via string-matching the 'yaml: ' prefix and invoking the debugger earlier, ensuring the actual compose parsing error is surfaced instead of a misleading project-name error.
Out of Scope Changes check ✅ Passed All changes directly support the objective: error handling refactoring in compose.go and session.go, a debug prompt update, and a minor formatting fix align with surfacing YAML parsing errors.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jordan/avoid-swallowing-invalid-compose-file-error

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cmd/cli/command/compose.go (1)

84-90: ⚠️ Potential issue | 🔴 Critical

Critical: Missing return causes nil pointer dereference.

When LoadProject fails, handleInvalidComposeFileErr is called but its result is not returned. Execution continues to line 90 where project.Name is accessed, but project is nil when loadErr != nil, causing a panic.

🐛 Proposed fix
 			project, loadErr := session.Loader.LoadProject(ctx)
 			if loadErr != nil {
-				handleInvalidComposeFileErr(ctx, loadErr)
+				return handleInvalidComposeFileErr(ctx, loadErr)
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cmd/cli/command/compose.go` around lines 84 - 90, When LoadProject
returns an error, the code calls handleInvalidComposeFileErr but continues and
dereferences project causing a panic; update the error branch after calling
handleInvalidComposeFileErr to stop further execution (add a return) so you
don't call canIUseProvider with a nil project. Specifically modify the block
around session.Loader.LoadProject(ctx) / handleInvalidComposeFileErr to return
immediately on loadErr, ensuring project.Name and project.Services are only
accessed when project is non-nil before invoking canIUseProvider.
🧹 Nitpick comments (1)
src/pkg/cli/compose/loader.go (1)

127-129: Consider adding a comment explaining the string-based detection.

The string prefix check is a pragmatic workaround since compose-go doesn't expose a specific error type for YAML parsing failures (as noted in the PR description). A brief comment would help future maintainers understand this is intentional.

💡 Suggested comment
+		// compose-go doesn't expose a specific error type for YAML parsing failures,
+		// so we detect them by checking for the "yaml: " prefix in the error message.
 		if strings.HasPrefix(err.Error(), "yaml: ") {
 			return nil, ErrInvalidComposeFile{fmt.Errorf("failed to parse compose file: %w", err)}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pkg/cli/compose/loader.go` around lines 127 - 129, Add a brief
explanatory comment above the strings.HasPrefix(err.Error(), "yaml: ") check in
src/pkg/cli/compose/loader.go clarifying that this string-based detection is an
intentional pragmatic workaround because compose-go does not expose a dedicated
YAML parse error type; reference the rationale (compose-go missing typed error)
and mention that the check distinguishes YAML parse errors from other errors in
the function that returns ErrInvalidComposeFile so future maintainers understand
it’s deliberate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cmd/cli/command/session.go`:
- Around line 142-159: The function handleInvalidComposeFileErr is shadowing the
original compose load error by reassigning the parameter err when calling
CreateProjectForDebug(); change the code so you do not overwrite the incoming
err—store the project loader error in a new variable (e.g., projectErr or
loaderErr) or use a short-named project variable for the project while keeping
the original err intact, then pass the preserved original err into
debugger.DebugComposeLoadError; ensure NewDebugger and CreateProjectForDebug
return values are handled with distinct variable names so the original compose
error propagated into DebugComposeLoadError is not lost.

---

Outside diff comments:
In `@src/cmd/cli/command/compose.go`:
- Around line 84-90: When LoadProject returns an error, the code calls
handleInvalidComposeFileErr but continues and dereferences project causing a
panic; update the error branch after calling handleInvalidComposeFileErr to stop
further execution (add a return) so you don't call canIUseProvider with a nil
project. Specifically modify the block around session.Loader.LoadProject(ctx) /
handleInvalidComposeFileErr to return immediately on loadErr, ensuring
project.Name and project.Services are only accessed when project is non-nil
before invoking canIUseProvider.

---

Nitpick comments:
In `@src/pkg/cli/compose/loader.go`:
- Around line 127-129: Add a brief explanatory comment above the
strings.HasPrefix(err.Error(), "yaml: ") check in src/pkg/cli/compose/loader.go
clarifying that this string-based detection is an intentional pragmatic
workaround because compose-go does not expose a dedicated YAML parse error type;
reference the rationale (compose-go missing typed error) and mention that the
check distinguishes YAML parse errors from other errors in the function that
returns ErrInvalidComposeFile so future maintainers understand it’s deliberate.

@jordanstephens jordanstephens force-pushed the jordan/avoid-swallowing-invalid-compose-file-error branch from fb02138 to 858e56b Compare February 18, 2026 20:15
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/cmd/cli/command/session.go (1)

147-149: Consider adding a comment explaining the string-matching rationale.

The prefix matching for "yaml: " and "validating " is fragile and depends on compose-go's error message format. A brief comment would help future maintainers understand why this approach is used and what to watch for if compose-go changes.

📝 Suggested comment
+	// compose-go doesn't provide typed errors for YAML parsing failures,
+	// so we detect them by matching known error prefixes. If compose-go
+	// changes its error format, this detection may need updating.
 	if !strings.HasPrefix(err.Error(), "yaml: ") && !strings.HasPrefix(err.Error(), "validating ") {
 		return err
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cmd/cli/command/session.go` around lines 147 - 149, Add a brief inline
comment above the conditional that checks err.Error() prefixes ("yaml: " and
"validating ") in session.go explaining that this string-matching is
intentionally used to detect specific compose-go error messages
(parsing/validation) and is fragile because it relies on compose-go's error text
format; mention what kind of errors it intends to allow through (yaml
parse/validation), that changes in compose-go error wording would require
updating these prefixes, and suggest adding a TODO linking to compose-go issue
or replacing with typed/errors when compose-go exposes structured error types;
place this comment immediately above the if using the exact symbols
strings.HasPrefix(err.Error(), "yaml: ") and strings.HasPrefix(err.Error(),
"validating ") to make the rationale discoverable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/cmd/cli/command/session.go`:
- Around line 142-168: Preserve the original parsing error in
handleInvalidComposeFileErr by avoiding reassignment of the incoming err;
capture the original error into a new variable (e.g., originalErr := err) at the
top, then call compose.NewLoader().CreateProjectForDebug() and
debug.NewDebugger(...) into distinct variables (e.g., project, loadErr and
debugger, dbgErr) instead of reusing err, and pass originalErr into
debugger.DebugComposeLoadError and return originalErr at the end so the original
YAML/validation error is not lost.

---

Nitpick comments:
In `@src/cmd/cli/command/session.go`:
- Around line 147-149: Add a brief inline comment above the conditional that
checks err.Error() prefixes ("yaml: " and "validating ") in session.go
explaining that this string-matching is intentionally used to detect specific
compose-go error messages (parsing/validation) and is fragile because it relies
on compose-go's error text format; mention what kind of errors it intends to
allow through (yaml parse/validation), that changes in compose-go error wording
would require updating these prefixes, and suggest adding a TODO linking to
compose-go issue or replacing with typed/errors when compose-go exposes
structured error types; place this comment immediately above the if using the
exact symbols strings.HasPrefix(err.Error(), "yaml: ") and
strings.HasPrefix(err.Error(), "validating ") to make the rationale
discoverable.

@jordanstephens jordanstephens merged commit ebb91af into main Feb 18, 2026
1 of 2 checks passed
@jordanstephens jordanstephens deleted the jordan/avoid-swallowing-invalid-compose-file-error branch February 18, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Confusing error when there's a typo in compose file

2 participants