# How Project-Level .qwen/settings.json Overrides User-Level Configuration in Qwen Code

> Learn how project-level .qwen/settings.json overrides user-level configuration in Qwen Code. Discover the deep-merge algorithm and precedence rules for workspace settings.

- Repository: [Qwen/qwen-code](https://github.com/qwenlm/qwen-code)
- Tags: internals
- Published: 2026-02-19

---

**Project-level [`.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/.qwen/settings.json) overrides user-level settings through a deep-merge algorithm that gives workspace-specific keys precedence while inheriting undefined values from the global configuration.**

As implemented in QwenLM/qwen-code, the CLI employs a hierarchical configuration system that loads settings from two distinct scopes. Understanding how project-level [`.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/.qwen/settings.json) overrides user-level configuration allows developers to manage sensitive credentials and environment-specific behaviors without duplicating entire configuration files.

## Configuration File Locations

Qwen Code resolves settings from two filesystem locations, distinguished by scope and accessed through specific `Storage` class methods:

| Scope | File Location | Resolution Method |
|-------|---------------|-------------------|
| **User (global)** | `~/.qwen/settings.json` | `Storage.getGlobalSettingsPath()` returns `<home-dir>/.qwen/settings.json` |
| **Project (workspace)** | `<project-root>/.qwen/settings.json` | `Storage.getWorkspaceSettingsPath()` returns `<cwd>/.qwen/settings.json` |

According to the source code in [`packages/core/src/config/storage.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/config/storage.ts), the workspace path is defined on lines 18-20, while the global path retrieval operates on lines 40-42. These methods provide the canonical paths used throughout the configuration loading lifecycle.

## The Configuration Loading Process

The `loadSettings()` function in [`packages/cli/src/config/settings.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/cli/src/config/settings.ts) (approximately lines 900-960) orchestrates the merge operation. This function implements a deterministic loading strategy that ensures project-level values always take precedence.

### Step-by-Step Merge Logic

The loader executes three distinct phases:

1. **Load global settings first** – The user-level file at `~/.qwen/settings.json` is parsed into a base configuration object.
2. **Load workspace settings if present** – The project-level file at [`./.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/./.qwen/settings.json) is read only if it exists on disk.
3. **Deep-merge with precedence** – The workspace configuration is merged onto the global configuration using a deep-merge utility (similar to `lodash.merge`), where project-level keys replace identical keys from the user-level file.

The resulting configuration follows the pattern `finalSettings = { ...global, ...workspace }` with nested objects recursively merged rather than shallow-replaced.

## Practical Code Examples

### Manual Configuration Loading

You can replicate the merge behavior manually using the core storage utilities:

```typescript
import { readFileSync } from 'node:fs';
import { Storage } from '@/core/config/storage';
import merge from 'lodash.merge';

// Load user-level settings
let userSettings = {};
try {
  userSettings = JSON.parse(
    readFileSync(Storage.getGlobalSettingsPath(), 'utf8')
  );
} catch { /* ignore if file does not exist */ }

// Load project-level settings (if present)
let workspaceSettings = {};
try {
  workspaceSettings = JSON.parse(
    readFileSync(Storage.getWorkspaceSettingsPath(), 'utf8')
  );
} catch { /* ignore if file does not exist */ }

// Deep-merge – workspace overrides user
const finalSettings = merge({}, userSettings, workspaceSettings);

```

### Using the Built-in Loader

For production use, import the CLI's optimized loader:

```typescript
import { loadSettings } from '@/cli/config/settings';

const cwd = process.cwd();          // e.g., /my/project
const settings = loadSettings(cwd); // returns the merged config
console.log(settings);

```

### Override Example with API Keys

Consider this configuration scenario demonstrating how project-level [`.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/.qwen/settings.json) overrides user-level configuration:

```json
// ~/.qwen/settings.json
{
  "modelProviders": {
    "openai": { "apiKey": "USER_KEY" }
  },
  "telemetry": { "enabled": false }
}

```

```json
// ./.qwen/settings.json (project-specific)
{
  "modelProviders": {
    "openai": { "apiKey": "PROJECT_KEY" }
  },
  "telemetry": { "enabled": true }
}

```

After the merge operation, `settings.modelProviders.openai.apiKey` resolves to `"PROJECT_KEY"` and `settings.telemetry.enabled` evaluates to `true`. The project-level values completely replace their global counterparts while preserving any user-level settings not explicitly overridden.

## Key Source Files

Understanding the override mechanism requires familiarity with these specific source locations:

| File | Role |
|------|------|
| [`packages/core/src/config/storage.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/config/storage.ts) | Defines `getGlobalSettingsPath()` and `getWorkspaceSettingsPath()` for filesystem resolution |
| [`packages/cli/src/config/settings.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/cli/src/config/settings.ts) | Implements `loadSettings()` with the deep-merge logic that enforces project-level precedence |
| [`docs/users/configuration/settings.md`](https://github.com/QwenLM/qwen-code/blob/main/docs/users/configuration/settings.md) | Documents the two-level configuration model and override behavior for end users |

## Summary

- **Project-level [`.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/.qwen/settings.json) takes precedence** over `~/.qwen/settings.json` through a deep-merge operation performed by `loadSettings()` in the CLI package.
- **The merge is non-destructive** – undefined project-level values inherit from the global configuration, while defined values completely replace global counterparts at any nesting level.
- **Path resolution** is handled by `Storage.getGlobalSettingsPath()` and `Storage.getWorkspaceSettingsPath()` in [`packages/core/src/config/storage.ts`](https://github.com/QwenLM/qwen-code/blob/main/packages/core/src/config/storage.ts).
- **Nested objects are recursively merged** rather than shallow-copied, allowing granular overrides of specific provider settings without redefining entire configuration blocks.

## Frequently Asked Questions

### What happens if the project-level settings.json is missing?

If `<project-root>/.qwen/settings.json` does not exist, the CLI uses only the user-level configuration from `~/.qwen/settings.json`. The `loadSettings()` function wraps the workspace file read in a try-catch block, ensuring the absence of a project file does not prevent startup.

### Does the override work for deeply nested configuration objects?

Yes. The merge algorithm uses deep-merge logic (similar to `lodash.merge`) that recursively combines nested objects. This means you can override a single property like `modelProviders.openai.apiKey` without losing other provider settings defined in the global configuration.

### Can I prevent specific global settings from being inherited?

No. The current implementation does not support explicit nullification or deletion of global keys through the project-level file. To effectively disable a global setting, you must explicitly set it to a neutral value (such as an empty string or false) in the project-level configuration rather than omitting it.

### Where should I store sensitive API keys?

Store sensitive API keys in the project-level [`.qwen/settings.json`](https://github.com/QwenLM/qwen-code/blob/main/.qwen/settings.json) file and add that file to your `.gitignore`. This prevents credentials from being committed to version control while allowing different team members to maintain their own global defaults in `~/.qwen/settings.json`.