multica-ai/multica · error

import skill: %w

Error message

import skill: %w

What it means

Returned by the `--file` branch of `multica skill import`: the archive was read from disk successfully, but the multipart upload via `client.ImportSkillFile` to `/api/skills/import` failed. Before this generic wrap, `handleSkillImportError` inspects the error for a name-conflict (409) to produce a friendlier message — reaching this line means the failure was NOT a simple conflict: it is a transport error, timeout (60s budget), invalid archive, or a 4xx/5xx from the server.

Source

Thrown at server/cmd/multica/cmd_skill.go:482

	onConflict, _ := cmd.Flags().GetString("on-conflict")
	if !validSkillImportConflictStrategy(onConflict) {
		return fmt.Errorf("--on-conflict must be one of: fail, overwrite, rename, skip")
	}

	ctx, cancel := context.WithTimeout(context.Background(), cli.AtLeastAPITimeout(60*time.Second))
	defer cancel()

	var result map[string]any
	if importFile != "" {
		fileData, readErr := os.ReadFile(importFile)
		if readErr != nil {
			return fmt.Errorf("read skill archive: %w", readErr)
		}
		if err := client.ImportSkillFile(ctx, fileData, filepath.Base(importFile), onConflict, &result); err != nil {
			if handledErr := handleSkillImportError(cmd, err); handledErr != nil {
				return handledErr
			}
			return fmt.Errorf("import skill: %w", err)
		}
		return printSkillImportResult(cmd, result)
	}

	body := map[string]any{
		"url":         importURL,
		"on_conflict": onConflict,
	}
	if err := client.PostJSON(ctx, "/api/skills/import", body, &result); err != nil {
		if handledErr := handleSkillImportError(cmd, err); handledErr != nil {
			return handledErr
		}
		return fmt.Errorf("import skill: %w", err)
	}

	return printSkillImportResult(cmd, result)
}

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Re-check server reachability/auth with `multica skill list` from the same environment.
  2. Validate the archive structure: it must contain the expected skill bundle layout (SKILL.md at the expected level) — rebuild it with the same tool that produced working imports.
  3. If a timeout, retry on a stable connection or import from `--url` instead to let the server fetch directly.
  4. If the server returned 400/500, check server logs for the concrete validation failure — the CLI message only wraps the status.

Example fix

# before
multica skill import --file bundle.zip --on-conflict fail  # 400 invalid bundle

# after
unzip -l bundle.zip          # confirm expected skill files at top level
multica skill import --file fixed-bundle.zip --on-conflict fail
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-flight: server reachable, archive looks like a zip with expected layout
multica skill list > /dev/null || exit 1
command -v unzip >/dev/null && unzip -l "$ARCHIVE" | grep -q SKILL.md || echo 'warning: SKILL.md not visible in archive'

Try / catch

Branch on the wrapped cause: conflict-shaped errors are already handled by handleSkillImportError, so what remains is transport (fix env), 400 invalid-bundle (fix archive layout), or timeout (retry / switch to --url). Log server-side details when the status is 4xx/5xx since the CLI message is generic.

Prevention

When it happens

Trigger: Server unreachable or auth failure; archive is not a valid skill bundle (missing SKILL.md, bad structure) so the server rejects it with 400; upload exceeding the 60s context deadline on slow links or large bundles; server 500 while persisting files.

Common situations: Hand-zipped bundle with wrong top-level layout; local dev server restarted mid-upload; bundle built with a newer format than the running server understands (version skew between CLI and server); network proxy stripping multipart bodies.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/2166d9ac8998ea96. Report an issue: GitHub.