FuelLabs/fuels-ts · error · FuelError

WORKSPACE_NOT_DETECTED

WORKSPACE_NOT_DETECTED

Error message

Forc workspace not detected in:
  ${workspace}/Forc.toml

Try using '${swayProgramType}s' instead of 'workspace' in:
  ${configPath}

What it means

Thrown during config resolution when fuels.config.ts declares a `workspace` but the Forc.toml at that workspace path has no `[workspace]` section. The loader reads the manifest, sees no workspace members, and tells you the path points at a single Sway program (contract/script/predicate) rather than a workspace.

Source

Thrown at packages/fuels/src/cli/config/loadConfig.ts:100

  if (!userConfig.workspace) {
    // Resolve members individually
    const { contracts, predicates, scripts } = userConfig;
    config.contracts = (contracts || []).map((c: string) => resolve(cwd, c));
    config.scripts = (scripts || []).map((s: string) => resolve(cwd, s));
    config.predicates = (predicates || []).map((p: string) => resolve(cwd, p));
  } else {
    // Resolve members via workspace
    const workspace = resolve(cwd, userConfig.workspace);
    const forcToml = readForcToml(workspace);

    if (!forcToml.workspace) {
      const workspaceMsg = `Forc workspace not detected in:\n  ${workspace}/Forc.toml`;

      const swayProgramType = readSwayType(workspace);
      const exampleMsg = `Try using '${swayProgramType}s' instead of 'workspace' in:\n  ${configPath}`;

      throw new FuelError(
        FuelError.CODES.WORKSPACE_NOT_DETECTED,
        [workspaceMsg, exampleMsg].join('\n\n')
      );
    }

    const swayMembers = forcToml.workspace.members.map((member) => resolve(workspace, member));

    swayMembers
      .map((path) => ({ path, type: readSwayType(path) }))
      .filter(({ type }) => type !== SwayType.library)
      .forEach(({ path, type }) => config[`${type as Exclude<SwayType, 'library'>}s`].push(path));

    config.workspace = workspace;
  }

  return config;
}

View on GitHub (pinned to b3f37c91ac)

Solutions

  1. Switch the config key from `workspace` to the matching plural key (`contracts`, `scripts`, or `predicates`) — the error message tells you the detected type.
  2. Or restructure the program into a real Forc workspace with a `[workspace] members = [...]` section.

Example fix

// before — Forc.toml has [contract], not [workspace]
export default createConfig({ workspace: './my-contract' });
// after
export default createConfig({ contracts: ['./my-contract'] });
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
function isWorkspace(path: string): boolean {
  if (!existsSync(join(path, 'Forc.toml'))) return false;
  return /\[workspace\]/.test(readFileSync(join(path, 'Forc.toml'), 'utf8'));
}

Type guard

const isWorkspaceConfig = (cfg: unknown): cfg is { workspace: string } =>
  !!cfg && typeof cfg === 'object' && typeof (cfg as any).workspace === 'string';

Prevention

When it happens

Trigger: Setting `workspace: './path'` in fuels.config.ts where ./path/Forc.toml describes a single program (it has `[contract]`/`[script]`/`[predicate]`, not `[workspace]`).

Common situations: Configuring a single program as a workspace, copied config from a workspace template into a single-program repo, refactored a workspace down to one program without updating config.

Related errors


AI-assisted analysis of FuelLabs/fuels-ts@b3f37c91ac (2026-08-12). Data as JSON: /api/errors/c784e4430e576a07. Report an issue: GitHub.