instructure/canvas-lms · error · RuntimeError
Unable to find external tool to import content.
Error message
Unable to find external tool to import content.
What it means
Raised by Lti::ContentMigrationService::Importer#load_tool! when the original ContextExternalTool exists but Lti::ToolFinder.from_url cannot resolve a tool for the course whose content_migration is configured, or the resolved tool lacks content_migration_configured?. It means Canvas cannot find a valid external tool in the target course to send the imported content to.
Solutions
- Install/re-install the external tool in the target course's account (or course) so ToolFinder can resolve it by domain/url.
- Verify the tool's configuration includes the content_migration settings (import_start_url) — re-add them in the tool XML or app settings.
- Check the tool is available in the target course's context and not disabled at account/sub-account level.
- Confirm @original_tool_id points to an existing ContextExternalTool on this Canvas instance (not a stale ID from another environment).
- Use preferred_tool_id matching (reinstalled tools) or re-copy the course after the tool is properly configured.
Example fix
# before: import into account lacking the tool
# "Unable to find external tool to import content."
# after: install the tool in the destination account first, then re-run the import
tool = account.context_external_tools.create!(name: 'Vendor Tool', domain: 'tool.example.com', settings: { content_migration: { import_start_url: 'https://tool.example.com/import' } }) Defensive patterns
Strategy: validation
Validate before calling
# check before calling send_imported_content
original_tool = ContextExternalTool.find_by(id: original_tool_id)
resolved = original_tool && Lti::ToolFinder.from_url(original_tool.domain, course, preferred_tool_id: original_tool_id)
unless resolved&.content_migration_configured?
raise "Tool #{original_tool_id} is not installed/configured for content migration in #{course.name}; install it first."
end Type guard
def tool_ready_for_import?(course, original_tool_id) original_tool = ContextExternalTool.find_by(id: original_tool_id) return false unless original_tool tool = Lti::ToolFinder.from_url(original_tool.domain, course, preferred_tool_id: original_tool_id) !tool.nil? && tool.content_migration_configured? end
Try / catch
begin
importer.send_imported_content(course, migration, content)
rescue RuntimeError => e
if e.message == 'Unable to find external tool to import content.'
# prompt admin to install the tool in the destination account, or skip this tool's content
flag_missing_tool(original_tool_id)
else
raise
end
end Prevention
- Always install and configure the external tool in the destination account before importing a course copy.
- Verify content_migration settings exist in the tool's configuration XML.
- Check tool availability (not disabled) in the target course's context chain.
- When migrating between environments, remap original_tool_ids to the tools installed in that environment.
When it happens
Trigger: Calling send_imported_content with an original_tool_id whose tool (a) was deleted (ContextExternalTool.find raises RecordNotFound upstream or the domain lookup fails), (b) is not installed/available in the target course's account chain, or (c) is found but has no content_migration configuration (no import settings in its tool XML/settings).
Common situations: Importing a course copy into a different Canvas account/instance where the external tool was never installed; the tool was uninstalled or replaced between export and import (tool reinstallation changes IDs); tool configuration XML updated and the content_migration block removed; admin disabled the tool in the target course.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Unable to start import for external tool #
- no matching external tool found
- Unable to start import for external tool #
- Use Lti::ResourceLink#current_external_tool to lookup…
- A partner ID is required to use Academic Benchmarks
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/d1d9cfefdff0e13c.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/lti/content_migration_service/importer.rb:102
end
private
def start_import_post_body(content)
{
context_id: Lti::V1p1::Asset.opaque_identifier_for(@course),
data: content,
tool_consumer_instance_guid: @root_account.lti_guid,
}.merge(expanded_variables)
end
def load_tool!
return @tool if @tool
original_tool = ContextExternalTool.find(@original_tool_id)
@tool = Lti::ToolFinder.from_url(original_tool.domain, @course, preferred_tool_id: @original_tool_id).tap do |t|
unless t&.content_migration_configured?
raise "Unable to find external tool to import content."
end
end
end
def import_start_url
@import_url ||= @tool.settings.dig(:content_migration, :import_start_url)
end
def import_format
@tool.settings.dig(:content_migration, :import_format)
end
end
end
end
View on GitHub (pinned to 1c9f0bb801)