Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fb6265c
fix(config): catch parse errors gracefully during startup
HaleTom May 25, 2026
de8dc4a
fix(config): convert tests to it.instance pattern matching upstream
HaleTom May 25, 2026
85ed2e4
fix(config): narrow catchCause to parse/schema only, strengthen tests
HaleTom May 25, 2026
109a6eb
fix(config): catch plugin resolution errors per-file to preserve mult…
HaleTom May 25, 2026
e1406e0
fix(console): bill google non-stream zen usage (#28829)
MrMushrooooom May 25, 2026
0373ea9
feat(acp): implement acp-next session slice (#29250)
nexxeln May 25, 2026
00ea47a
chore: generate
opencode-agent[bot] May 25, 2026
56743dc
fix(acp): share acp-next session state (#29253)
nexxeln May 25, 2026
e426b11
feat(tui): make prompt size responsive and configurable (#28255)
bjschafer May 25, 2026
054ca3a
chore: generate
opencode-agent[bot] May 25, 2026
24a0fe7
perf: use redis/upstash for ip rate limits (#28694)
vimtor May 25, 2026
68d1833
chore: generate
opencode-agent[bot] May 25, 2026
d5b6345
test(acp): add compatibility baseline (#29222)
nexxeln May 25, 2026
658b938
chore: generate
opencode-agent[bot] May 25, 2026
5477b4e
chore: update nix node_modules hashes
opencode-agent[bot] May 25, 2026
ca9d857
feat(acp): add initial acp-next skeleton behind runtime flag (#29226)
nexxeln May 25, 2026
416f01d
feat(acp-next): add content conversion helpers (#29231)
nexxeln May 25, 2026
ed010b7
test(acp-next): add config option helpers (#29234)
nexxeln May 25, 2026
ae594f9
feat(acp-next): add pure tool conversion helpers (#29232)
nexxeln May 25, 2026
72c1693
fix(acp-next): map typed errors to request errors (#29233)
nexxeln May 25, 2026
ddddceb
chore: generate
opencode-agent[bot] May 25, 2026
cb2006f
feat(acp-next): add directory snapshot service (#29241)
nexxeln May 25, 2026
fbfa968
feat(acp-next): add session state service (#29240)
nexxeln May 25, 2026
292b626
chore: generate
opencode-agent[bot] May 25, 2026
8fefc50
feat(acp-next): add usage service (#29249)
nexxeln May 25, 2026
6515990
chore: generate
opencode-agent[bot] May 25, 2026
45456fa
fix(config): skip auto-injection on parse failure
HaleTom May 25, 2026
7068379
Merge remote-tracking branch 'upstream/dev' into json-errors
HaleTom May 25, 2026
3922a82
fix(config): sanitize log.error to avoid leaking config content
HaleTom May 25, 2026
6378082
fix(config): use Cause.pretty for safe config error logging
HaleTom May 25, 2026
702fd0e
fix(config): address review comments on PR #29208
HaleTom May 25, 2026
a5ea910
fix(acp-next): add config switch fast paths (#29255)
nexxeln May 25, 2026
b11570a
fix(config): address copilot review comments
HaleTom May 25, 2026
a72cb35
fix(config): warn for each unknown key stripped from config
HaleTom May 25, 2026
b8eedcf
fix(config): add infoKeys sync comment, drop unused cause param
HaleTom May 25, 2026
998034f
fix(config): complete infoKeys allowlist, add safe error detail to logs
HaleTom May 25, 2026
828e255
fix(config): derive config keys dynamically from Info schema AST
HaleTom May 25, 2026
a99ebd1
fix(httpapi): model optional session payloads as no content (#29247)
kitlangton May 25, 2026
b46cec2
fix(httpapi): model optional worktree payload as no content (#29246)
kitlangton May 25, 2026
7753211
refactor(httpapi): describe bodyless global upgrade payload
kitlangton May 25, 2026
dabf2dc
remove the need for polling from experimental background agents (#29179)
rekram1-node May 25, 2026
d96e3a5
chore: generate
opencode-agent[bot] May 25, 2026
0bfa55b
tweak (config): make modalities input/output fields optional so that …
robposch May 25, 2026
d595e47
chore: generate
opencode-agent[bot] May 25, 2026
633b5d6
fix: allow experimental flags to override umbrella (#29273)
rekram1-node May 25, 2026
5b02ac4
feat: initial datalake and stats site (#28666)
adamdotdevin May 25, 2026
16a3004
chore: generate
opencode-agent[bot] May 25, 2026
1ed1415
chore: update nix node_modules hashes
opencode-agent[bot] May 25, 2026
3c7f608
fix: aws stages
adamdotdevin May 25, 2026
2b3ddf9
chore: cleanup
adamdotdevin May 25, 2026
b0fcba5
feat(app): make server sdk + sync state global (#29285)
Brendonovich May 26, 2026
4862c3e
chore: remove gh role from infra
adamdotdevin May 26, 2026
87e9e70
fix(opencode): revert google sdk tool call id change (#29310)
rekram1-node May 26, 2026
dbb7872
chore: update nix node_modules hashes
opencode-agent[bot] May 26, 2026
49707c2
fix(app): deduplicate and merge server connections in resolveServerLi…
Brendonovich May 26, 2026
15c5ec6
feat(acp): add acp-next session lifecycle (#29320)
nexxeln May 26, 2026
245f00a
chore: generate
opencode-agent[bot] May 26, 2026
7e5305c
feat(acp-next): add event routing (#29327)
nexxeln May 26, 2026
8845a43
chore: generate
opencode-agent[bot] May 26, 2026
b5632ea
fix(config): fallback when user info unavailable (#29332)
nexxeln May 26, 2026
717e74f
feat(acp): stream acp-next tool updates (#29333)
nexxeln May 26, 2026
c71fe78
fix(app): disable health check for web deployments (#29319)
Brendonovich May 26, 2026
1f66db0
test(app): restore timeline e2e server health mock (#29336)
Hona May 26, 2026
13c9d09
feat(app): refine desktop v2 home and session controls (#28788)
Hona May 26, 2026
ca354f8
chore: generate
opencode-agent[bot] May 26, 2026
b660374
Merge branch 'dev' into json-errors
HaleTom May 26, 2026
7dbb8f9
fix(config): include source path in stripUnknownKeys warning log
HaleTom May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,26 @@ export const layer = Layer.effect(
: { text, type: "virtual", ...options, env },
),
)
const parsed = ConfigParse.jsonc(expanded, source)
const data = ConfigParse.schema(Info, normalizeLoadedConfig(parsed, source), source)
const data = yield* Effect.sync(() => {
const parsed = ConfigParse.jsonc(expanded, source)
return ConfigParse.schema(Info, normalizeLoadedConfig(parsed, source), source)
}).pipe(
Comment thread
HaleTom marked this conversation as resolved.
Effect.catchCause((cause) =>
Effect.sync(() => {
log.error("invalid config", { path: source, cause })
return Schema.decodeSync(Info)({})
}),
Comment thread
HaleTom marked this conversation as resolved.
Comment thread
HaleTom marked this conversation as resolved.
Comment thread
HaleTom marked this conversation as resolved.
),
Comment thread
HaleTom marked this conversation as resolved.
)
Comment thread
HaleTom marked this conversation as resolved.
if (!("path" in options)) return data

yield* Effect.promise(() => resolveLoadedPlugins(data, options.path))
yield* Effect.promise(() => resolveLoadedPlugins(data, options.path)).pipe(
Effect.catchCause((cause) =>
Effect.sync(() => {
log.error("plugin resolution failed", { path: source, cause })
}),
),
)
if (!data.$schema) {
data.$schema = "https://opencode.ai/config.json"
const updated = text.replace(/^\s*\{/, '{\n "$schema": "https://opencode.ai/config.json",')
Expand Down
49 changes: 43 additions & 6 deletions packages/opencode/test/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,24 +580,61 @@ accountTokenIt.instance("resolves env templates in account config with account t
}),
)

it.instance("validates config schema and throws on invalid fields", () =>
it.instance("handles invalid schema gracefully without crashing", () =>
Effect.gen(function* () {
const test = yield* TestInstance
yield* writeConfigEffect(test.directory, {
$schema: "https://opencode.ai/config.json",
invalid_field: "should cause error",
})
const exit = yield* Config.use.get().pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true)
const config = yield* Config.use.get()
expect(config.username).toBeDefined()
expect("invalid_field" in config).toBe(false)
}),
Comment thread
HaleTom marked this conversation as resolved.
)

it.instance("throws error for invalid JSON", () =>
it.instance("handles invalid JSON gracefully without crashing", () =>
Effect.gen(function* () {
const test = yield* TestInstance
yield* AppFileSystem.use.writeWithDirs(path.join(test.directory, "opencode.json"), "{ invalid json }")
const exit = yield* Config.use.get().pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true)
const config = yield* Config.use.get()
expect(config.username).toBeDefined()
}),
)

it.instance("handles invalid JSONC syntax gracefully without crashing", () =>
Effect.gen(function* () {
const test = yield* TestInstance
yield* AppFileSystem.use.writeWithDirs(
path.join(test.directory, "opencode.jsonc"),
`{
// comment
"model": "test/model",
"username": "testuser",`,
)
const config = yield* Config.use.get()
expect(config.username).toBeDefined()
}),
)

it.instance("skips bad config file but merges others", () =>
Effect.gen(function* () {
const test = yield* TestInstance
yield* AppFileSystem.use.writeWithDirs(
path.join(test.directory, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
model: "global/model",
username: "globaluser",
}),
)
yield* AppFileSystem.use.writeWithDirs(
path.join(test.directory, "opencode.jsonc"),
"{ invalid json }",
)
const config = yield* Config.use.get()
expect(config.model).toBe("global/model")
expect(config.username).toBe("globaluser")
}),
)

Expand Down
Loading