Skip to content

config is not initialized after JSON unmarshal succeeds #1133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

KevyVo
Copy link
Contributor

@KevyVo KevyVo commented Apr 28, 2025

Description

  • If the JSON unmarshal succeeds, but the file on disk does not contain the MCPServers field (or it is null in the JSON), then after unmarshal, config.MCPServers will be nil. This is a subtle bug: If the config file exists and is missing the MCPServers field, unmarshal will not set it, leaving it as nil.

  • Then, when I try to assign to config.MCPServers["defang"], causing the panic.

  • Solution: map instantiation guard before assignment.

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

- If the JSON unmarshal succeeds,
but the file on disk does not contain the MCPServers
field (or it is null in the JSON), then after unmarshal,
config.MCPServers will be nil. This is a subtle bug:
If the config file exists and is missing the MCPServers field,
unmarshal will not set it, leaving it as nil.

- Then, when I try to assign to config.MCPServers["defang"],
causing the panic.

- Solution: map instantiation guard before assignment.
@KevyVo KevyVo self-assigned this Apr 28, 2025
@KevyVo KevyVo changed the title configis not initialized after JSON unmarshal succeeds config is not initialized after JSON unmarshal succeeds Apr 28, 2025
Copy link
Member

@lionello lionello left a comment

Choose a reason for hiding this comment

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

Add test with repro

KevyVo added 2 commits April 28, 2025 16:41
This will test many of the edge cases that were previously
not tested, including:
- Incorrect/misspelled client name
- If the client exists or not
- If the config file exists or not
- If the config file is empty or not
- Check the config contents
- Check for expected errors
Copy link
Member

@lionello lionello left a comment

Choose a reason for hiding this comment

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

LBYL (Look Before You Leap) and EAFP (Easier to Ask Forgiveness than Permission) are two coding styles for handling errors. LBYL involves checking conditions before performing actions, while EAFP focuses on trying to perform actions and handling exceptions if they occur, with EAFP being the preferred idiomatic approach: don't do Stat before reading the file.

Comment on lines 102 to 109
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json"), nil
case "windows":
return filepath.Join(getAppData(), "Claude", "claude_desktop_config.json"), nil
default:
return filepath.Join(getConfigHome(), "Claude", "claude_desktop_config.json"), nil
}
Copy link
Member

Choose a reason for hiding this comment

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

dedupe

@@ -258,13 +248,21 @@ func SetupClient(client string) error {

term.Infof("Updating %q\n", configPath)

// Create the directory if it doesn't exist
// Create the directory path
Copy link
Member

Choose a reason for hiding this comment

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

Looks like you never create it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is mislabeled now thinking about it, it should say "create the string to the file's directory" instead.

info, err := os.Stat(configDir)
if err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("The client %s you are trying to setup is not install or not found in your system path, please try again after installing.", client)
Copy link
Member

Choose a reason for hiding this comment

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

spelling; no capital and no punctuation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry I was just when you said "no capital and no punctuation". Do you mean like this needs capitation and punctuation or we do not include capitation and punctuation in our errors?

Copy link
Member

Choose a reason for hiding this comment

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

The go linter doesn’t like it when errors start with a capital or end with punctuation

return fmt.Errorf("failed to check config directory: %w", err)
}
if !info.IsDir() {
return fmt.Errorf("Config path %s exists but is not a directory, please try again after installing or update the client %s to support mcp.", configDir, client)
Copy link
Member

Choose a reason for hiding this comment

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

spelling; no capital and no punctuation

@KevyVo
Copy link
Contributor Author

KevyVo commented May 1, 2025

LBYL (Look Before You Leap) and EAFP (Easier to Ask Forgiveness than Permission) are two coding styles for handling errors. LBYL involves checking conditions before performing actions, while EAFP focuses on trying to perform actions and handling exceptions if they occur, with EAFP being the preferred idiomatic approach: don't do Stat before reading the file.

I see. Thank you for giving me feedback on my erroring handling. I will try and and catch if it fails then.

KevyVo added 5 commits May 2, 2025 11:03
- Added more testcases to setup
- Move error handling to EAFP
- Need to add a test for the getClientConfigPath function in setup.go
- The test should check if the function correctly returns the path to the client config file
@KevyVo KevyVo force-pushed the kevin/mcpMapBug branch 5 times, most recently from 1c2d6af to 76667dd Compare May 6, 2025 00:31
@KevyVo KevyVo force-pushed the kevin/mcpMapBug branch from 76667dd to e3ef97d Compare May 6, 2025 05:12
}

// For testing purposes, this can be overridden
var currentOS = runtime.GOOS
Copy link
Member

Choose a reason for hiding this comment

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

Instead of checking GOOS in code, let's use Go build tags (filename suffix)

@KevyVo
Copy link
Contributor Author

KevyVo commented May 22, 2025

Update on this, we are going to be using prefix like colonizer.

@KevyVo KevyVo linked an issue May 22, 2025 that may be closed by this pull request
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.

Unit Testing
2 participants