vercel/ai · error

ACP source.pnpmWorkspaceYaml must not be empty.

Error message

ACP source.pnpmWorkspaceYaml must not be empty.

What it means

createACPV1 validates that an 'npm-locked' ACP source carries a non-empty pnpmWorkspaceYaml string. The npm-locked source type reproduces a workspace install from checked-in pnpm-lock.yaml and pnpm-workspace.yaml files, so an empty pnpm-workspace.yaml means the launch cannot faithfully reconstruct the dependency graph. This guard fails fast at construction instead of at install time.

Solutions

  1. Provide the actual contents of the repo's pnpm-workspace.yaml in source.pnpmWorkspaceYaml.
  2. If the project has no workspace file, omit the field entirely (undefined) instead of passing an empty string.
  3. Check the code that reads the file — it is swallowing errors and returning '' — and make it throw or skip the field.

Example fix

// before
createACPV1({ source: { type: 'npm-locked', packageJson, pnpmLockYaml, pnpmWorkspaceYaml: fs.readFileSync('pnpm-workspace.yaml', 'utf8') } }); // returns '' if missing
// after
createACPV1({ source: { type: 'npm-locked', packageJson, pnpmLockYaml, pnpmWorkspaceYaml: fs.existsSync('pnpm-workspace.yaml') ? fs.readFileSync('pnpm-workspace.yaml', 'utf8') : undefined } });
Defensive patterns

Strategy: validation

Validate before calling

if (source.type === 'npm-locked' && source.pnpmWorkspaceYaml != null && source.pnpmWorkspaceYaml.length === 0) {
  throw new Error('pnpmWorkspaceYaml must be non-empty or omitted');
}

Type guard

function hasPnpmWorkspaceYaml(source) {
  return source.type !== 'npm-locked' || source.pnpmWorkspaceYaml == null || source.pnpmWorkspaceYaml.length > 0;
}

Try / catch

try {
  const impl = createACPV1(settings);
} catch (err) {
  if (err instanceof Error && err.message.includes('pnpmWorkspaceYaml')) {
    console.error('Fix npm-locked source: provide pnpm-workspace.yaml contents or omit the field');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling createACPV1 with source.type === 'npm-locked' and source.pnpmWorkspaceYaml set to an empty string ('' or ' '). Note: undefined/null is allowed (the check is ?.length === 0), only explicitly-empty strings throw.

Common situations: Loading pnpm-workspace.yaml with a read that returns '' on failure (e.g. fs.readFileSync defaulting), building config programmatically from environment variables that are unset-to-empty, or copying config from a repo without a pnpm-workspace.yaml.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/edc6e0ca7334e77c. Report an issue: GitHub.

Appendix: source

Thrown at packages/harness-acp/src/v1/implementation.ts:66

  /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
const PACKAGE_NAME_REGEXP =
  /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
const EXECUTABLE_NAME_REGEXP = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
const ENVIRONMENT_VARIABLE_NAME_REGEXP = /^[A-Za-z_][A-Za-z0-9_]*$/;

export function validateACPV1Implementation(
  implementation: ACPImplementation,
): void {
  const { source } = implementation;
  if (source.type === 'npm-locked') {
    if (source.packageJson.length === 0) {
      throw new Error('ACP source.packageJson must not be empty.');
    }
    if (source.pnpmLockYaml.length === 0) {
      throw new Error('ACP source.pnpmLockYaml must not be empty.');
    }
    if (source.pnpmWorkspaceYaml?.length === 0) {
      throw new Error('ACP source.pnpmWorkspaceYaml must not be empty.');
    }
  } else if (source.type === 'npm-simple') {
    validateNpmSimpleSource({ source });
  } else if (source.command.trim().length === 0) {
    throw new Error('ACP source.command must not be empty.');
  }
  if (!EXECUTABLE_NAME_REGEXP.test(implementation.executable)) {
    throw new Error(
      `ACP executable must be a bare command name without a path; received ${JSON.stringify(implementation.executable)}.`,
    );
  }

  validateForwardEnvironment({ forwardEnv: implementation.forwardEnv });
  validateForwardEnvironment({ forwardEnv: implementation.credentialEnv });
  validateEnvironment({ env: implementation.env });
  const forwardedKeys = new Set(implementation.forwardEnv ?? []);
  const credentialKeys = new Set(implementation.credentialEnv ?? []);
  for (const key of credentialKeys) {

View on GitHub (pinned to 69428b1f8b)