multica-ai/multica · error

upsert skill file: %w

Error message

upsert skill file: %w

What it means

Returned by `multica skill files upsert` when the PUT to `/api/skills/{id}/files` with `{path, content}` fails. Client-side validation already guaranteed non-empty path and content, so this is a server-side or transport failure: 404 (bad skill ID or path rejected), 400 (path violates server rules, e.g. absolute path, `..` traversal, or disallowed extension), auth, or timeout.

Source

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

	content, hasContent, err := resolveSkillContentFlag(cmd)
	if err != nil {
		return err
	}
	if !hasContent || content == "" {
		return fmt.Errorf("--content is required")
	}

	body := map[string]any{
		"path":    filePath,
		"content": content,
	}

	ctx, cancel := cli.APIContext(context.Background())
	defer cancel()

	var result map[string]any
	if err := client.PutJSON(ctx, "/api/skills/"+args[0]+"/files", body, &result); err != nil {
		return fmt.Errorf("upsert skill file: %w", err)
	}

	output, _ := cmd.Flags().GetString("output")
	if output == "json" {
		return cli.PrintJSON(os.Stdout, result)
	}

	fmt.Printf("Skill file upserted: %s (%s)\n", strVal(result, "path"), strVal(result, "id"))
	return nil
}

func runSkillFilesDelete(cmd *cobra.Command, args []string) error {
	client, err := newAPIClient(cmd)
	if err != nil {
		return err
	}

	ctx, cancel := cli.APIContext(context.Background())

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Make --path bundle-relative and traversal-free (no leading `/`, no `..`): e.g. `references/api.md`.
  2. Confirm the skill ID exists via `multica skill get <id>`.
  3. For large content, pipe through the file/stdin content source instead of a giant inline flag.
  4. Read the wrapped error: 400 = payload/path rejected, 404 = bad ID, connection errors = environment.

Example fix

# before
multica skill files upsert sk-1 --path /home/me/notes.md --content "..."

# after
multica skill files upsert sk-1 --path notes.md --content "..."
Defensive patterns

Strategy: validation

Validate before calling

case "$BUNDLE_PATH" in /*|*..*) echo 'path must be bundle-relative without ..'; exit 1;; esac
[ "${#CONTENT}" -lt 1000000 ] || { echo 'content too large for inline flag; use a file source'; exit 1; }
multica skill get "$SKILL_ID" > /dev/null || exit 1

Try / catch

Branch on wrapped status: 400 → fix the path shape (relative, no traversal, allowed extension); 404 → re-resolve the skill ID; connection/timeout → environment or content size. Capture the server's response detail from the error text before retrying anything.

Prevention

When it happens

Trigger: Upserting to a skill ID that does not exist; using --path `/etc/passwd`, `../secrets`, or another traversal-shaped value the server rejects; very large content exceeding server body limits; transport/auth failure.

Common situations: Using a local absolute file path as --path instead of a bundle-relative one; migrating content between environments with different skill IDs; pasting a multi-megabyte file as an inline --content argument exceeding shell or server limits.

Related errors


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