different-ai/openwork · error

scope must be 'project' or 'global'

Error message

scope must be 'project' or 'global'

What it means

resolveOpencodeConfigPath only accepts scope values 'project' or 'global'; anything else falls into the final else and throws. This is an explicit argument validation to prevent silently resolving config in an unintended location.

Solutions

  1. Change scope to exactly 'project' or 'global' (lowercase).
  2. If the call came from commands-style code using 'workspace', switch it to 'project' for config APIs.
  3. Validate/normalize the scope string at the call site before invoking.
  4. Use a union type or constant enum for scope in renderer code so invalid values fail at compile time.

Example fix

// before
await resolveOpencodeConfigPath("workspace", dir);
// after
await resolveOpencodeConfigPath("project", dir);
Defensive patterns

Strategy: validation

Validate before calling

const VALID_CONFIG_SCOPES = ['project', 'global'];
if (!VALID_CONFIG_SCOPES.includes(scope)) {
  throw new Error(`scope must be one of ${VALID_CONFIG_SCOPES.join(', ')}, got ${scope}`);
}

Type guard

function isConfigScope(scope) {
  return scope === 'project' || scope === 'global';
}

Prevention

When it happens

Trigger: Calling a config-path API with scope misspelled ('Project', 'workspace', 'user'), undefined/null scope, or a scope value carried over from the commands API which uses 'workspace' instead of 'project'.

Common situations: Copy-pasting code between the commands API (scope 'workspace'|'global') and the config API (scope 'project'|'global'), typos in string literals, or dynamic scope values derived from unvalidated settings.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/046be3c58be3eb65. Report an issue: GitHub.

Appendix: source

Thrown at apps/desktop/electron/main.mjs:1468

    runtimeBootstrapPromise = bootRuntimeForSelectedWorkspace().catch((error) => ({
      ok: false,
      error: error instanceof Error ? error.message : String(error),
    }));
  }
  return runtimeBootstrapPromise;
}

function resolveOpencodeConfigPath(scope, projectDir) {
  if (scope === "project") {
    if (!String(projectDir ?? "").trim()) {
      throw new Error("projectDir is required");
    }
    return workspaceOpencodeConfigCandidates(projectDir);
  } else if (scope === "global") {
    const root = globalOpencodeRoot();
    return [path.join(root, "opencode.jsonc"), path.join(root, "opencode.json")];
  } else {
    throw new Error("scope must be 'project' or 'global'");
  }
}

async function selectOpencodeConfigPath(candidates) {
  for (const candidate of candidates) {
    if (await pathExists(candidate)) return candidate;
  }
  return candidates[0];
}

async function readOpencodeConfig(scope, projectDir) {
  const chosenPath = await selectOpencodeConfigPath(resolveOpencodeConfigPath(scope, projectDir));
  const exists = await pathExists(chosenPath);
  return {
    path: chosenPath,
    exists,
    content: exists ? await readFile(chosenPath, "utf8") : null,
  };

View on GitHub (pinned to 2b7df46e8a)