{"record":{"id":"4892e2af8b89fafd","repo":"multica-ai/multica","slug":"import-source-temporarily-unavailable","errorCode":null,"errorMessage":"import source temporarily unavailable","messagePattern":"import source temporarily unavailable","errorType":"error_code","errorClass":null,"httpStatus":503,"severity":"warning","filePath":"server/internal/handler/skill.go","lineNumber":630,"sourceCode":"\tbundleSize  int            // running sum of file content bytes for cap enforcement\n\torigin      map[string]any // written into skill.config.origin so the UI can show provenance\n}\n\ntype importedFile struct {\n\tpath    string\n\tcontent string\n}\n\n// errImportCapExceeded marks an error caused by a per-file or per-bundle cap.\n// Such errors must abort the import — silently dropping a file would otherwise\n// produce an incomplete skill that looks valid to the user.\nvar errImportCapExceeded = errors.New(\"import cap exceeded\")\n\n// errImportSourceUnavailable marks a transient failure to read the upstream\n// source (e.g. the GitHub tree API rate limiting). The import can't proceed\n// safely but should be retried, so it maps to a retryable HTTP status rather\n// than a permanent error.\nvar errImportSourceUnavailable = errors.New(\"import source temporarily unavailable\")\n\n// isCapError reports whether err is (or wraps) errImportCapExceeded.\nfunc isCapError(err error) bool {\n\treturn errors.Is(err, errImportCapExceeded)\n}\n\n// addFile appends a supporting file while enforcing the per-bundle caps. It\n// returns an error when either the file count or aggregate byte budget would\n// be exceeded so the caller fails the import instead of silently truncating.\n//\n// Binary files (images, fonts, archives) are silently skipped: their bytes\n// can't survive a PG TEXT column (SQLSTATE 22021), and they're reference\n// assets the agent never reads as text anyway. Logging the skip leaves a\n// breadcrumb if a user expected one of these to import.\nfunc (s *importedSkill) addFile(path, content string) error {\n\tif isLikelyBinaryFilePath(path) {\n\t\tslog.Info(\"skill import: skipping binary file\", \"path\", path, \"size\", len(content))\n\t\treturn nil","sourceCodeStart":612,"sourceCodeEnd":648,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/handler/skill.go#L612-L648","documentation":"errImportSourceUnavailable marks a transient failure to read the upstream import source, classically the GitHub tree API rate limiting an unauthenticated or heavily-used client. The import cannot proceed safely now but should be retried later, so the handler maps it to a retryable HTTP status (5xx-class) rather than a permanent 400.","triggerScenarios":"POST a GitHub skill import while the GitHub API is rate-limited (403 with x-ratelimit-remaining: 0), the tree API 5xx's, or the network drops mid-fetch. Repeating the identical request after the rate-limit window succeeds.","commonSituations":"Bulk-importing many skills in a loop from one IP; CI without a GITHUB_TOKEN; importing right after a burst of other GitHub API calls; transient upstream outages.","solutions":["Retry with backoff after the rate-limit reset (honor x-ratelimit-reset when present)","Configure a GitHub token for the importer so the rate ceiling rises dramatically","Batch imports with delays instead of a tight loop"],"exampleFix":"// before\nfor (const repo of repos) await importSkill(repo); // burns the rate limit\n// after\nfor (const repo of repos) {\n  await importSkill(repo);\n  await sleep(2000);\n  // on 503/429-style retryable failure: exponential backoff and retry once or twice\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function importWithRetry(ref, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await importSkill(ref); }\n    catch (e) {\n      const retryable = e.status === 503 || e.status === 429 || /temporarily unavailable/i.test(e.message);\n      if (!retryable || i === attempts - 1) throw e;\n      await sleep(2 ** i * 1000);\n    }\n  }\n}","preventionTips":["Configure a GitHub token for the importer to raise rate ceilings","Space out bulk imports instead of tight loops","Honor x-ratelimit-reset when present; do not hammer during a limit window"],"tags":["import","rate-limit","github","retryable","skills"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}