different-ai/openwork · error

skill name must be kebab-case

Error message

skill name must be kebab-case

What it means

validateSkillName() enforces that a skill's directory/name is lowercase kebab-case: non-empty and matching /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Names with uppercase, spaces, underscores, leading/trailing hyphens, or empty strings throw this error.

Source

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

    output += `description: ${escapeYamlScalar(command.description.trim())}\n`;
  }
  if (typeof command?.agent === "string" && command.agent.trim()) {
    output += `agent: ${escapeYamlScalar(command.agent.trim())}\n`;
  }
  if (typeof command?.model === "string" && command.model.trim()) {
    output += `model: ${escapeYamlScalar(command.model.trim())}\n`;
  }
  if (command?.subtask === true) {
    output += "subtask: true\n";
  }
  output += `---\n\n${template}\n`;
  return output;
}

function validateSkillName(raw) {
  const trimmed = String(raw ?? "").trim();
  if (!trimmed || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(trimmed)) {
    throw new Error("skill name must be kebab-case");
  }
  return trimmed;
}

const runtimeManager = createRuntimeManager({
  app,
  desktopRoot: path.resolve(__dirname, ".."),
  listLocalWorkspacePaths: () => workspaceStore.listLocalWorkspacePaths(),
  // When OPENWORK_ENCRYPTION_KEY is set, skip the safeStorage provider so it does not shadow the documented env override used by CI/headless/enterprise.
  localManagedMcpVaultKey: process.env.OPENWORK_ENCRYPTION_KEY?.trim()
    ? undefined
    : createDesktopVaultKeyProvider({
        filePath: path.join(app.getPath("userData"), "local-managed-mcp-vault-key.bin"),
        loadSafeStorage: () => require("electron").safeStorage,
      }),
});
const initialRunnerBootstrap = workspaceStore.readDesktopBootstrapConfigSync();
const legacyRunnerBaseUrls = [

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Rename the skill to lowercase kebab-case, e.g. "My Skill" → "my-skill".
  2. Replace underscores with hyphens and strip leading/trailing hyphens.
  3. Slugify the name before validation: lowercase, replace non-alphanumerics with '-', collapse repeats.
  4. Sanitize at the creation entry point so invalid names are rejected or transformed before persisting.

Example fix

// before
validateSkillName('My_Cool Skill'); // throws
// after
const name = 'My_Cool Skill'
  .toLowerCase()
  .replace(/[^a-z0-9]+/g, '-')
  .replace(/^-+|-+$/g, '');
validateSkillName(name); // 'my-cool-skill'
Defensive patterns

Strategy: validation

Validate before calling

const KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
if (!KEBAB.test(name.trim())) throw new TypeError('skill name must be kebab-case');

Type guard

function isKebabCase(v) {
  return typeof v === 'string' && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(v);
}

Try / catch

try {
  validateSkillName(rawName);
} catch (err) {
  if (String(err.message) === 'skill name must be kebab-case') {
    console.error('Invalid skill name:', rawName, '→ use lowercase-with-hyphens');
  } else throw err;
}

Prevention

When it happens

Trigger: Creating, importing, or syncing a skill whose name is "My Skill", "my_skill", "MySkill", "-skill", "skill-", or an empty/undefined value passed to validateSkillName().

Common situations: Users naming skills with spaces or CamelCase in the UI; importing skills from external sources that allow other conventions (e.g. snake_case); filesystem-derived names with non-ASCII characters; refactors leaving name undefined.

Related errors


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