{"record":{"id":"b01fc26f75c1e4de","repo":"multica-ai/multica","slug":"upsert-skill-file-w","errorCode":null,"errorMessage":"upsert skill file: %w","messagePattern":"upsert skill file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/cmd/multica/cmd_skill.go","lineNumber":700,"sourceCode":"\tcontent, hasContent, err := resolveSkillContentFlag(cmd)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !hasContent || content == \"\" {\n\t\treturn fmt.Errorf(\"--content is required\")\n\t}\n\n\tbody := map[string]any{\n\t\t\"path\":    filePath,\n\t\t\"content\": content,\n\t}\n\n\tctx, cancel := cli.APIContext(context.Background())\n\tdefer cancel()\n\n\tvar result map[string]any\n\tif err := client.PutJSON(ctx, \"/api/skills/\"+args[0]+\"/files\", body, &result); err != nil {\n\t\treturn fmt.Errorf(\"upsert skill file: %w\", err)\n\t}\n\n\toutput, _ := cmd.Flags().GetString(\"output\")\n\tif output == \"json\" {\n\t\treturn cli.PrintJSON(os.Stdout, result)\n\t}\n\n\tfmt.Printf(\"Skill file upserted: %s (%s)\\n\", strVal(result, \"path\"), strVal(result, \"id\"))\n\treturn nil\n}\n\nfunc runSkillFilesDelete(cmd *cobra.Command, args []string) error {\n\tclient, err := newAPIClient(cmd)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctx, cancel := cli.APIContext(context.Background())","sourceCodeStart":682,"sourceCodeEnd":718,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/cmd/multica/cmd_skill.go#L682-L718","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make --path bundle-relative and traversal-free (no leading `/`, no `..`): e.g. `references/api.md`.","Confirm the skill ID exists via `multica skill get <id>`.","For large content, pipe through the file/stdin content source instead of a giant inline flag.","Read the wrapped error: 400 = payload/path rejected, 404 = bad ID, connection errors = environment."],"exampleFix":"# before\nmultica skill files upsert sk-1 --path /home/me/notes.md --content \"...\"\n\n# after\nmultica skill files upsert sk-1 --path notes.md --content \"...\"","handlingStrategy":"validation","validationCode":"case \"$BUNDLE_PATH\" in /*|*..*) echo 'path must be bundle-relative without ..'; exit 1;; esac\n[ \"${#CONTENT}\" -lt 1000000 ] || { echo 'content too large for inline flag; use a file source'; exit 1; }\nmultica skill get \"$SKILL_ID\" > /dev/null || exit 1","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["Use bundle-relative paths without leading slash or '..' segments.","Resolve skill IDs fresh before upserting.","Route large content through file/stdin sources instead of a giant inline --content string."],"tags":["cli","http","skill","files","upsert"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}